提高 Postgresql 查询性能
Posted
技术标签:
【中文标题】提高 Postgresql 查询性能【英文标题】:Improve Postgre SQL query performance 【发布时间】:2021-08-03 21:23:27 【问题描述】:我正在我们的数据库中运行此查询:
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)
输出以下计划:
逻辑是:对于每个选择的id_pr
(在 500 个 id 的列表中)计算与其关联的整数列 pb.nr_size
的总和,返回该数量与数字 2,147,483,647 之间的较小值。结果必须包含 500 行,每个 id 一行,并且我们已经知道它们将匹配子查询中的至少一行,因此不会产生空值。
索引tb_pr_dc_in05
只是id_pr
上的一个b-tree,它是整数类型。索引tb_pr_dc_bn_pk
只是主键id_pr_dc_bn
上的b-tree,它也是整数类型。表 tb_pr_dc
对于每个 id_pr
都有很多行。实际上,我们在 tb_pr_dc
中有 209,217 个唯一的 id_pr
s,总共 13,910,855 行。表tb_pr_dc_bn
的行数相同。
可以看出,我们定义了 500 个 id 来查询 tb_pr_dc
,找到 109,468 行(不到表大小的 1%),然后在 tb_pr_dc_bn
中找到相同的数量。 Imo,索引看起来很好,要评估的行数很少,所以我不明白为什么运行这个查询要花这么多时间。许多其他查询在其他表上读取更多数据并进行更多计算运行良好。 DBA 刚刚运行了重新索引和真空分析,但它仍然以同样缓慢的方式运行。我们在 Linux 上运行 PostgreSQL 11。我在没有并发访问的副本中运行此查询。
我可能缺少什么可以提高此查询性能?
感谢您的关注。
【问题讨论】:
样本数据、预期结果和清晰的逻辑解释会有所帮助。 您需要向我们展示表和索引定义,以及每个表的行数。也许您的表格定义不佳。也许索引没有正确创建。也许您认为您在该列上没有索引。没有看到表和索引定义,我们无法判断。我们需要行计数,因为这会影响查询计划。least(2147483647, sum(pb.nr_size))
应该做什么?如果 SUM 为 NULL,您是否尝试分配一个值? COALESCE 是用于此目的的正确工具。
我正在编辑以解释查询的逻辑。
为什么是子查询?您可以在 where 子句中直接使用列表。为什么会有无用的1=1
条件?我认为您可以将查询简化为this。 tb_pr_dc (id_pr, id_pr_dc_bn)
和 tb_pr_dc_bn (id_pr_dc_bn)
上的索引应该会有所帮助
【参考方案1】:
时间花在跳遍整个表格以找到 109468 个随机分散的行,发出随机 IO 请求来这样做。您可以验证是否打开 track_io_timing 并重做计划(可能只是让它全局打开,默认情况下,开销很低,它产生的价值很高),但我确信我不需要在得出这个结论之前先看看那个输出。其他更快的查询可能访问更少的磁盘页面,因为它们访问的数据更紧凑,或者被组织以便可以更按顺序读取。事实上,考虑到它必须阅读多少页,我会说你的查询相当快。
您问为什么在计划的内部节点中输出了这么多列。这样做的原因是 PostgreSQL 通常只是传递指向元组在 shared_buffers 中所在位置的指针,并且被指向的元组具有表本身具有的列。它可以分配内存来存储重新格式化的元组版本,去掉不必要的列,但这通常会更多,而不是更少。如果无论如何复制和重新形成元组是一个原因,它会在这样做时删除无关的列。但它不会无缘无故地这样做。
加快此速度的一种方法是创建索引以启用仅索引扫描。那就是on tb_pr_dc (id_pr, id_pr_dc_bn)
和on tb_pr_dc_bn (id_pr_dc_bn, nr_size)
。
如果这还不够,可能还有其他方法可以改进;但是,如果我一直被表名和列名的一长串难以记住的、不可发音的乱码分散注意力,我就无法思考这些问题。
【讨论】:
关于提问者,我同意你关于难以阅读的列名和表名的评论。关于查询的推理对于强大的软件至关重要。选择有意义的名字!以上是关于提高 Postgresql 查询性能的主要内容,如果未能解决你的问题,请参考以下文章