[英]Improve Postgre SQL query performance
我在我们的数据库中运行这个查询:
select
(
select least(2147483647, sum(pb.nr_size))
from tb_pr_dc pd
inner join tb_pr_dc_bn pb on 1=1
and pb.id_pr_dc_bn = pd.id_pr_dc_bn
where 1=1
and pd.id_pr = pt.id_pr -- outer query column
)
from
(
select regexp_split_to_table('[list of 500 ids]', ',')::integer id_pr
) pt
;
它输出具有单个结果列的 500 行,运行大约需要 1 分 43 秒。 explain (analyze, verbose, buffers)
输出以下计划:
Subquery Scan on pt (cost=0.00..805828.19 rows=1000 width=8) (actual time=96.791..103205.872 rows=500 loops=1) Output: (SubPlan 1) Buffers: shared hit=373771 read=153484 -> Result (cost=0.00..22.52 rows=1000 width=4) (actual time=0.434..3.729 rows=500 loops=1) Output: ((regexp_split_to_table('[list of 500 ids]', ',')::integer id_pr) -> ProjectSet (cost=0.00..5.02 rows=1000 width=32) (actual time=0.429..2.288 rows=500 loops=1) Output: (regexp_split_to_table('[list of 500 ids]', ',')::integer id_pr -> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1) SubPlan 1 -> Aggregate (cost=805.78..805.80 rows=1 width=8) (actual time=206.399..206.400 rows=1 loops=500) Output: LEAST('2147483647'::bigint, sum((pb.nr_size)::integer)) Buffers: shared hit=373771 read=153484 -> Nested Loop (cost=0.87..805.58 rows=83 width=4) (actual time=1.468..206.247 rows=219 loops=500) Output: pb.nr_size Inner Unique: true Buffers: shared hit=373771 read=153484 -> Index Scan using tb_pr_dc_in05 on db.tb_pr_dc pd (cost=0.43..104.02 rows=83 width=4) (actual time=0.233..49.289 rows=219 loops=500) Output: pd.id_pr_dc, pd.ds_pr_dc, pd.id_pr, pd.id_user_in, pd.id_user_ex, pd.dt_in, pd.dt_ex, pd.ds_mt_ex, pd.in_at, pd.id_tp_pr_dc, pd.id_pr_xz (...) Index Cond: ((pd.id_pr)::integer = pt.id_pr) Buffers: shared hit=24859 read=64222 -> Index Scan using tb_pr_dc_bn_pk on db.tb_pr_dc_bn pb (cost=0.43..8.45 rows=1 width=8) (actual time=0.715..0.715 rows=1 loops=109468) Output: pb.id_pr_dc_bn, pb.ds_ex, pb.ds_md_dc, pb.ds_m5_dc, pb.nm_aq, pb.id_user, pb.dt_in, pb.ob_pr_dc, pb.nr_size, pb.ds_sg, pb.ds_cr_ch, pb.id_user_ (...) Index Cond: ((pb.id_pr_dc_bn)::integer = (pd.id_pr_dc_bn)::integer) Buffers: shared hit=348912 read=89262 Planning Time: 1.151 ms Execution Time: 103206.243 ms
逻辑是:对于选择的每个id_pr
(在 500 个 id 的列表中)计算与它们关联的 integer 列pb.nr_size
的总和,返回该数量与数字 2,147,483,647 之间的较小值。 结果必须包含 500 行,每个 id 一行,并且我们已经知道它们将匹配子查询中的至少一行,因此不会产生 null 值。
索引tb_pr_dc_in05
只是id_pr
上的 b-tree,它是 integer 类型。 索引tb_pr_dc_bn_pk
只是主键id_pr_dc_bn
上的 b 树,它也是 integer 类型。 表tb_pr_dc
每个id_pr
都有很多行。 实际上,我们在tb_pr_dc
id_pr
总共 13,910,855 行。 表tb_pr_dc_bn
具有相同数量的行。
可以看出,我们定义了 500 个 id 来查询tb_pr_dc
,找到 109,468 行(不到表大小的 1%),然后在tb_pr_dc_bn
中找到相同的数量。 Imo,索引看起来很好,要评估的行数很少,所以我不明白为什么运行这个查询要花这么多时间。 许多其他查询在其他表上读取更多数据并进行更多计算运行良好。 DBA 刚刚运行了重新索引和真空分析,但它仍然以同样缓慢的方式运行。 我们在 Linux 上运行 PostgreSQL 11。 我在没有并发访问的副本中运行此查询。
我可能缺少什么可以提高此查询性能?
感谢您的关注。
时间花在跳遍表格以找到 109468 个随机分散的行,发出随机 IO 请求来这样做。 您可以验证是否打开 track_io_timing 并重做计划(可能只是让它全局打开,默认情况下,开销很低,它产生的价值很高),但我确信我不需要在得出这个结论之前,请先查看 output。 其他更快的查询可能访问更少的磁盘页面,因为它们访问的数据更紧凑,或者被组织以便可以更按顺序读取。 事实上,考虑到它必须阅读多少页,我会说你的查询相当快。
您问为什么计划的内部节点中有这么多列是output。 其原因是 PostgreSQL 通常只是传递指向元组在 shared_buffers 中所在位置的指针,并且指向的元组具有表本身具有的列。 它可以分配 memory 来存储元组的重新格式化版本,其中删除了不必要的列,但这通常会做更多的工作,而不是更少。 如果无论如何复制和重新形成元组是一个原因,它会在这样做时删除无关的列。 但它不会无缘无故地这样做。
加快此速度的一种方法是创建索引以启用仅索引扫描。 这些将on tb_pr_dc (id_pr, id_pr_dc_bn)
和on tb_pr_dc_bn (id_pr_dc_bn, nr_size)
。
如果这还不够,可能还有其他方法可以改进; 但是,如果我一直被表和列名的一长串难以记住的不可发音的胡言乱语分散注意力,我就无法思考它们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.