暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

等待 PostgreSQL 18 — 允许 EXPLAIN 显示分数行数

lovely girl 2025-03-04
36

在 2025 年 2 月 21 日,Robert Haas 提交了一个补丁:

允许 EXPLAIN 显示分数行数

当 nloops > 1 时,我们现在显示两位小数,而不是没有小数。这很重要,因为我们显示的实际上是 planstate->instrument->ntuples / nloops,而有时你真正想知道的是 planstate->instrument->ntuples。你可以通过将显示的行数乘以显示的 nloops 值来估算它,但由于显示的值被四舍五入,这使得估算不精确。即使我们显示两位额外的小数,它仍然是不精确的,但不精确的程度降低了。也许我们会在以后找到一种方法来进一步改进这种输出,但目前看来,这比什么都不做要好。

讨论链接: http://postgr.es/m/603c8f070905281830g2e5419c4xad2946d149e21f9d%40mail.gmail.com

然后,6 天后,他又提交了:

EXPLAIN:始终为行数显示两位小数

提交 ddb17e387aa28d61521227377b00f997756b8a27 试图避免在 nloops > 1 时才显示小数点后的数字,因为单次迭代后不可能有分数行数。然而,这使得回归测试变得不稳定,因为并行查询在正常情况下,Gather 或 Gather Merge 以下的所有节点的 nloops 都会 > 1,但如果工作进程没有及时启动,而主进程完成了所有工作,它们的 nloops 就会突然变成 1,这使得小数点后的数字是否显示变得不可预测。虽然 44cbba9a7f51a3888d5087fc94b23614ba2b81f2 似乎修复了即时失败,但可能仍存在较低概率的回归测试失败。

这里可以采用多种修复方法。例如,有人曾提议,如果 rows/nloops 是整数,则只显示小数点后的数字,但目前 rows 是以浮点数存储的,因此它在理论上不是一个精确的量——在极端情况下可能会丢失精度。还有人提议,如果我们在某种可能引起循环的结构下(无论它是否实际发生),则只显示小数点后的数字。尽管这些想法并非毫无道理,但这个补丁采用了更简单的解决方案,即始终显示两位小数。如果这种方法能够经受住构建农场和人类用户的审查,它可以为我们省去做更复杂事情的麻烦;如果不行,我们可以重新评估。

此提交偶然地撤销了 44cbba9a7f51a3888d5087fc94b23614ba2b81f2,该提交不再需要。

讨论链接: http://postgr.es/m/CA+TgmoazzVHn8sFOMFAEwoqBTDxKT45D7mvkyeHgqtoD2cn58Q@mail.gmail.com

以前,对于非常简单的查询,explain analyze 的输出看起来像这样:

=# explain analyze select 1;
                                     QUERY PLAN
------------------------------------------------------------------------------------
 Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
 Planning Time: 4.648 ms
 Execution Time: 0.354 ms
(3 rows)
复制

现在,它看起来像这样:

                                      QUERY PLAN
───────────────────────────────────────────────────────────────────────────────────────
 Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.002..0.002 rows=1.00 loops=1)
 Planning:
   Buffers: shared hit=3
 Planning Time: 0.113 ms
 Execution Time: 0.041 ms
(5 rows)
复制

区别在于它在“actual”部分显示 rows=1.00,而以前是 rows=1

为什么这很重要呢?对于一行的情况,这并不重要,但让我给你展示一些有趣的东西。假设我们有一个包含 5 行的表,如下所示:

=$ select * from data;
 v
───
 0
 0
 0
 0
 0
 1
(6 rows)
复制

现在,假设我们想从表中选择所有 v 为 1、2、3 和 4 的行。显然,只有 v == 1 的一行。为了使事情按我需要的方式工作,我必须强制 PostgreSQL 使用嵌套循环,但这都是可能的。经过一些调整后,这是 PostgreSQL 17 的 explain analyze 输出:

=$ explain analyze
SELECT
    w.*, x.*
FROM
    unnest('{1,2,3,4}'::INT4[]) with ordinality as w (q, idx),
    lateral (
        SELECT * FROM data WHERE v = q ORDER BY v desc LIMIT 1
    ) x;
                                                  QUERY PLAN
--------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.00..12.97 rows=4 width=16) (actual time=0.014..0.021 rows=1 loops=1)
   ->  Function Scan on unnest w  (cost=0.00..0.04 rows=4 width=12) (actual time=0.006..0.007 rows=4 loops=1)
   ->  Limit  (cost=0.00..3.22 rows=1 width=4) (actual time=0.003..0.003 rows=0 loops=4)
         ->  Seq Scan on data  (cost=0.00..41.88 rows=13 width=4) (actual time=0.002..0.002 rows=0 loops=4)
               Filter: (v = w.q)
               Rows Removed by Filter: 6
 Planning Time: 1.449 ms
 Execution Time: 0.051 ms
(8 rows)
复制

请注意,Seq Scan on data 显示为重复 4 次(loops=4),因为展开的数组有 4 个元素。它显示返回了 0 行。

这是因为实际的行数实际上是一个平均值。但由于 PostgreSQL < 18 不能显示分数,所以它显示为 0。这可能会导致行数计算出现问题。

在 PostgreSQL 18 中,explain 的输出看起来不同:

Nested Loop  (cost=0.00..12.97 rows=4 width=16) (actual time=0.017..0.025 rows=1.00 loops=1)
   Buffers: shared hit=4
   ->  Function Scan on unnest w  (cost=0.00..0.04 rows=4 width=12) (actual time=0.006..0.007 rows=4.00 loops=1)
   ->  Limit  (cost=0.00..3.22 rows=1 width=4) (actual time=0.003..0.004 rows=0.25 loops=4)
         Buffers: shared hit=4
         ->  Seq Scan on data  (cost=0.00..41.88 rows=13 width=4) (actual time=0.003..0.003 rows=0.25 loops=4)
               Filter: (v = w.q)
               Rows Removed by Filter: 6
               Buffers: shared hit=4
 Planning:
   Buffers: shared hit=4
 Planning Time: 0.142 ms
 Execution Time: 0.044 ms
(13 rows)
复制

请注意,它显示rows=0.25 - 乘以 4 后(因为 loops=4)得出该表所有循环中返回的行数正确为“1”。

这很棒。需要对explain.depesz.com进行小修复才能正确处理,但这绝对是一件好事。

原文地址:https://www.depesz.com/2025/02/28/waiting-for-postgresql-18-allow-explain-to-indicate-fractional-rows/

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论