提高大表过滤左外连接的查询性能

Jef*_*f G 2 postgresql optimization query-performance postgresql-performance postgresql-15

我正在尝试优化在 PostgreSQL 15.4 中连接两个大表(40MM+ 行)的查询。

\n
SELECT files.id, ARRAY_AGG(b.status)\nFROM files\nLEFT OUTER JOIN processing_tasks b\n    ON (files.id = b.file_id AND b.job_id = 113)\nWHERE files.round_id = 591\nGROUP BY files.id;\n
Run Code Online (Sandbox Code Playgroud)\n

explain (analyze)完全相同的查询的两个计划位于:

\n\n

在 中files,908,275 / 39,000,105 (2.3%) 个元组有round_id=591; 它是静态的。
\n在 中processing_tasks,4,026,364 / 60,780,802 (6.6%) 个元组有job_id=113,并且随着行的插入,这个值将变得越来越常见,可能达到表的 15%。

\n

这些链接上的“注释”选项卡包括表和索引定义,并显示数据pg_stats包括这些最常见的值。

\n

我对以下几个可能的目标中的任何一个都感到满意:

\n
    \n
  1. 使用索引扫描时花费的 3\xe2\x80\x934 秒是可以接受的,如果这是我能做的最好的事情,那么enable_seqscan即使在生产中我也应该继续覆盖吗?(我猜是在交易中)

    \n
  2. \n
  3. 但我宁愿进一步减少 3\xe2\x80\x934 秒,减少到 2 秒以下,并在processing_tasks增长时保持不变。

    \n
  4. \n
\n

jja*_*nes 5

这是一个奇怪的计划。奇怪的部分在于 depesz 搞砸了并行位图堆扫描的表示,仅显示其中一个工作人员的“缓冲区:”,就好像它是计划树整个部分的“缓冲区:”一样。转到“来源”选项卡会提供真实信息。

这仍然很奇怪。 Buffers: shared hit=108093没有读取。计划的这一部分需要近 850MB 的数据,而它的每一位恰好已经在共享缓冲区中?这是否只是因为您一遍又一遍地运行这个特定的查询参数化,但只向我们展示了它最快的运行?如果是这样,这似乎是一种非常不切实际的优化查询的方法。如果您从冷缓存运行它,或者如果您运行最近没有运行过的新参数化,会是什么样子?

没有办法告诉 PostgreSQL 期望在缓存中找到某个特定查询所需的所有数据。还有 effective_cache_size,但这是针对单个查询执行预计会重复命中相同数据的情况,而不是针对不同执行都命中相同数据的情况。您可以修改 seq 和随机 page_cost 设置,但您提出的任何设置都不太可能全局适用。如果您需要仅针对这一查询的范围调整设置,那么调整 enable_seqscan 而不是其他事情会更直接。

或者,您可以使用查询提示。有一个第三方扩展可以实现它们,https://github.com/ossc-db/pg_hint_plan。它甚至可以在 RDS 中使用;我不知道其他托管数据库提供商。

或者,(job_id, file_id,status) 上的索引应该在此表上启用仅索引扫描,如果许多页面被标记为全部可见,那么对于规划器来说,这可能比 seq 扫描更好。

对此的一些其他评论:如果您经常运行如此大的查询,您的 work_mem 似乎太低了;提高它不会解决 seqscan 问题,但可能会使快速计划更快。你的 IO 看起来很糟糕,40MB/s 看起来像是一台 15 年旧笔记本电脑的东西,而不是现代服务器级硬件(尽管这显然是针对每个工人的,所以实际上 120MB/s 更好,但仍然不是很好)。