在PostgreSQL中修改了一行不明显的代码,把(ANY(ARRAY[...]) 改成 ANY(VALUES(...))),结果查询时间从20s变为0.2s。最初我们学习使用EXPLAN ANALYZE来优化代码,到后来,Postgres社区也成为我们学习提升的一个好帮手,付出总会有回报,我们产品的性能也因此得到了极大的提升。
事出有因
我们所开发的产品是Datadog,它是专门为那些编写和运营大规模应用的团队、IT运营商提供监控服务的一个平台,帮助他们把海量的数据转化为切实可行的计划、操作方案。而在这周早些时候,我们的许多数据库所面临的一个性能问题是在一个较小的表上进行大量的key查询。这些查询中的99.9%都是高效灵活的。在极少数实例中,有些数量的性能指标tag查询是费时的,这些查询需要花费20s时间。这也就意味着用户需要在浏览器面前花费这么长的时间来等待图形编辑器做出响应。即使是0.1%,这样的用户体验也显然糟透了,对此,我们进行了监测,探究为何速度会这么慢。
查询与计划
结果令人震惊,罪魁祸首竟然是下面这个简单的查询:
SELECt c.key,
c.x_key,
c.tags,
x.name
FROM context c
JOIN x
ON c.x_key = x.key
WHERe c.key = ANY (ARRAY[
15368196
, --
11
,
000
other keys --)])
AND c.x_key =
1
AND c.tags @> ARRAY[E
'blah'
];
X表拥有上千行数据,C表拥有1500万行数据,这两个表的“key”列都带有适当的索引主键。简单地说,它就是一个简单的主键查询。但有趣地是,随着key列中记录的增加,例如在11000行时,我们通过添加EXPLAIN (ANALYZE, BUFFERS)前缀来查看key列的值是否与数组中的值匹配:
Nested Loop (cost=
6923.33
..
11770.59
rows=
1
width=
362
) (actual time=
17128.188
..
22109.283
rows=
10858
loops=
1
)
Buffers: shared hit=
83494
-> Bitmap Heap Scan on context c (cost=
6923.33
..
11762.31
rows=
1
width=
329
) (actual time=
17128.121
..
22031.783
rows=
10858
loops=
1
)
Recheck Cond: ((tags @>
'{blah}'
::text[]) AND (x_key =
1
))
Filter: (key = ANY (
'{15368196,(a lot more keys here)}'
::integer[]))
Buffers: shared hit=
50919
-> BitmapAnd (cost=
6923.33
..
6923.33
rows=
269
width=
0
) (actual time=
132.910
..
132.910
rows=
0
loops=
1
)
Buffers: shared hit=
1342
-> Bitmap Index Scan on context_tags_idx (cost=
0.00
..
1149.61
rows=
15891
width=
0
) (actual time=
64.614
..
64.614
rows=
264777
loops=
1
)
Index Cond: (tags @>
'{blah}'
::text[])
Buffers: shared hit=
401
-> Bitmap Index Scan on context_x_id_source_type_id_idx (cost=
0.00
..
5773.47
rows=
268667
width=
0
) (actual time=
54.648
..
54.648
rows=
267659
loops=
1
)
Index Cond: (x_id =
1
)
Buffers: shared hit=
941
-> Index Scan using x_pkey on x (cost=
0.00
..
8.27
rows=
1
width=
37
) (actual time=
0.003
..
0.004
rows=
1
loops=
10858
)
Index Cond: (x.key =
1
)
Buffers: shared hit=
32575
Total runtime:
22117.417
ms