为什么计划者会针对具有不同波动率的函数得出不同的结果?

Why is the planner coming up with different results for functions with different volatilities?

这个问题是 的跟进和结果。我应该注意,我不认为这是重复的,因为那个问题是在寻求特定问题的解决方案。我在这里询问有关一般行为的更多信息,并演示如何重现它。 (为了证明差异,您可以在我们讨论行为的已接受答案上看到相当长的评论线程,我觉得它离题了,尤其是考虑到长度。)

我有一个功能。这是一个展示感兴趣行为的示例:

CREATE OR REPLACE FUNCTION test(INT)
  RETURNS TABLE(num INT, letter TEXT)
  VOLATILE
  LANGUAGE SQL
  AS $$
  SELECT *
  FROM (VALUES (1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e')) x
  LIMIT 
  $$;

当我 运行 这个 EXPLAIN:

EXPLAIN ANALYZE SELECT * FROM test(10);

我在 psql 中得到了这个结果(我在其中删除了一个巨大的 "Query Plan" header):

 Function Scan on test  (cost=0.25..10.25 rows=1000 width=36) (actual time=0.125..0.136 rows=5 loops=1)
 Total runtime: 0.179 ms
(2 rows)

记下行估计。它估计有 1000 行。

但是,如果我将函数更改为 STABLEIMMUTABLE:

CREATE OR REPLACE FUNCTION test(INT)
  RETURNS TABLE(num INT, letter TEXT)
  STABLE
  LANGUAGE SQL
  AS $$
  SELECT *
  FROM (VALUES (1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e')) x
  LIMIT 
  $$;

然后同样的EXPLAIN给了我不同的方案:

 Limit  (cost=0.00..0.06 rows=5 width=36) (actual time=0.010..0.050 rows=5 loops=1)
   ->  Values Scan on "*VALUES*"  (cost=0.00..0.06 rows=5 width=36) (actual time=0.005..0.018 rows=5 loops=1)
 Total runtime: 0.087 ms
(3 rows)

现在它正确地估计了 5 行,并且它显示了函数中包​​含的查询的计划。成本要高一个数量级。 运行时间也减少了。 (查询很短,可能不是特别重要。)

鉴于链接的问题处理更多的数据并且具有非常显着的性能差异,看起来规划器实际上根据函数是 VOLATILE 还是 [=16 来做一些不同的事情=]/IMMUTABLE.

planner 在这里究竟在做什么,我在哪里可以阅读有关它的一些文档?

这些测试在 PG 9.3 中 运行。

估计有 1000 行

1000 估计行数是 CREATE FUNCTION:

中记录的默认值

execution_cost

A positive number giving the estimated execution cost for the function, in units of cpu_operator_cost. If the function returns a set, this is the cost per returned row. If the cost is not specified, 1 unit is assumed for C-language and internal functions, and 100 units for functions in all other languages. Larger values cause the planner to try to avoid evaluating the function more often than necessary.

result_rows

A positive number giving the estimated number of rows that the planner should expect the function to return. This is only allowed when the function is declared to return a set. The default assumption is 1000 rows.

当函数被声明为 volatile 时,它​​要求不被内联,因此 result_rows 的默认值成立。

另一方面,当它像在您的第二次测试中那样在查询中内联时,将估计行数,就好像函数体已移入查询并且函数声明没有'存在。由于可以直接评估 VALUES 子句,因此在第二个测试中会得出准确的估计值。

规划器到底在做什么,我在哪里可以阅读相关文档?

一般来说,规划器的优化策略在主文档中是没有解释的。它们在邮件列表中进行了讨论,并在源代码评论中提到,幸运的是,它们往往非常清晰且编写得很好(与一般源代码相比)。在函数内联的情况下,我相信 inline_set_returning_functions and inline_set_returning_function 的评论揭示了驱动此特定优化的大部分规则。 (警告:以上链接指向当前的 master 分支,随时可能更改或漂移)。