【问题标题】:Understanding COUNT behavior in queries vs. EXPLAIN vs. functions了解查询中的 COUNT 行为与 EXPLAIN 与函数
【发布时间】:2017-08-27 11:24:51
【问题描述】:

我很想了解(并可能改进)我在使用 PostgreSQL 9.6 时遇到的问题。名称已简化,但其他所有内容均取自 psql 会话。

我从物化视图开始,mv

首先,我创建了两个简单的函数:

CREATE FUNCTION count_mv() RETURNS BIGINT AS $$
SELECT COUNT(*) FROM mv;
$$ LANGUAGE SQL STABLE PARALLEL SAFE;

CREATE FUNCTION mv_pks() RETURNS TABLE (table_pk INTEGER) AS $$
SELECT table_pk FROM mv;
$$ LANGUAGE SQL STABLE PARALLEL SAFE;

让我们为一些查询计时。

db=>\timing on

我可以非常快速地计算物化视图的结果。

db=> SELECT COUNT(*) FROM mv;
  count
---------
 2567883
(1 row)

Time: 79.803 ms

让我们看看它是如何做到的。

db=> EXPLAIN ANALYZE SELECT COUNT(*) FROM mv;
                                                                  QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=41331.24..41331.25 rows=1 width=8) (actual time=765.681..765.681 rows=1 loops=1)
   ->  Gather  (cost=41330.62..41331.23 rows=6 width=8) (actual time=765.557..765.670 rows=7 loops=1)
         Workers Planned: 6
         Workers Launched: 6
         ->  Partial Aggregate  (cost=40330.62..40330.63 rows=1 width=8) (actual time=760.175..760.175 rows=1 loops=7)
               ->  Parallel Seq Scan on mv  (cost=0.00..39261.09 rows=427809 width=0) (actual time=0.014..397.952 rows=366840 loops=7)
 Planning time: 0.326 ms
 Execution time: 769.934 ms
(8 rows)

很好。所以它利用了多个工人。但是为什么使用EXPLAIN ANALYZE时查询会慢很多呢?

现在我使用count_mv() 函数,它具有相同 底层SQL 并声明为STABLE

db=> select count_mv();
  count_mv
------------
    2567883
(1 row)

Time: 406.058 ms

哇!为什么这比物化视图上的相同 SQL 慢?而且慢很多!是不是没有利用并行工作者,如果没有,为什么不呢?

开始编辑

按照下面的答案中的建议,我加载了auto_explain 模块并检查了函数调用中EXPLAIN 的日志输出。

    Query Text:
    SELECT COUNT(*) FROM mv;

     Finalize Aggregate  (cost=41331.60..41331.61 rows=1 width=8) (actual time=1345.446..1345.446 rows=1 loops=1)
       ->  Gather  (cost=41330.97..41331.58 rows=6 width=8) (actual time=1345.438..1345.440 rows=1 loops=1)
            Workers Planned: 6
            Workers Launched: 0
             ->  Partial Aggregate  (cost=40330.97..40330.99 rows=1 width=8) (actual time=1345.435..1345.435 rows=1 loops=1)
                  ->  Parallel Seq Scan on mv  (cost=0.00..39261.38 rows=427838 width=0) (actual time=0.020..791.022 rows=2567883 loops=1)

新问题是为什么计划了 6 名工人,但没有人启动。否则服务器空闲,配置相同,查询相同。

结束编辑

好的。那么如果我这样做呢:

db=> SELECT COUNT(*) FROM mv_pks();
  count
---------
 2567883
(1 row)

Time: 72.687 ms

与不使用EXPLAIN ANALYZE 直接在物化视图上计算行数相同的性能,但您必须在这里相信我:此函数的性能取决于创建函数时物化视图的状态。这里的快速计时是表为空时创建函数的结果。如果我在表已满时重新创建函数,该函数需要超过 1000 毫秒才能运行!

总结一下我的问题:

  1. 为什么STABLE SQL 函数内部的 SQL 查询比该函数外部的查询慢得多。
  2. 为什么在使用EXPLAIN ANALYZE 时,SQL 查询会这么慢?
  3. 为什么在计算某个函数的行数时会得到所有不同的结果,该函数可以与物化视图上的行数计算相当快,或者比任何其他方法慢,具体取决于函数的创建时间?

提前致谢!

【问题讨论】:

    标签: postgresql count sql-function explain postgresql-9.6


    【解决方案1】:

    对于 1),您可以使用 auto_explain 查找自己,它可以显示函数内部的查询计划。是否使用并行计划?

    对于 2),这是测量的开销,取决于平台,但可能很高。

    对于 3) 比较两种情况下的 SQL 计划。 SQL 函数中的查询没有被缓存,所以我没有解释为什么它应该这样。您是否多次重复测试以排除您看到从磁盘读取与从缓存读取的效果?

    【讨论】:

    • 感谢您的回复。 1)很棒的模块。这对很多事情都非常有帮助。关于新的解释输出缺乏并行性背后的原因有什么想法吗? 2) 明白了。说得通;惊讶它几乎是 10 倍。 3) 是的。我多次尝试测试。我什至有两个具有相同 SQL 但创建时间不同的函数,我可以一个接一个地来回运行并获得这些不同的结果。奇怪!
    • 第 3 点)我仍然不清楚。我进行了实验,但无法重现它。你能想出一个可重现的测试用例吗?
    • 我为 1) 添加了一个编辑。获奖:你能评论一下为什么一个函数中的相同 SQL 可能不会使用它计划的并行性吗?还是我误解了 EXPLAIN 输出? 3)您在测试用例中看到了哪些行为?不,我无法可靠地重现这一点,而且我知道这不是很令人满意。昨天,我无法创建具有快速性能的函数。今天,我无法创建性能缓慢的产品。然而,具有不同性能的现有函数显示与\df+ 相同的代码。
    • 我得到 Workers Planned = Workers Launched = 1 而不是 2 在交互式查询中。我不知道为什么从PARALLEL SAFE 函数调用会导致...并行查询仍然有其奇怪之处,它可能是未记录的限制或错误。
    • 是的,我想我会称之为错误/限制,然后在另一个版本中再次检查并关闭它。 PostgreSQL 是一款非常棒的软件。感谢您抽出宝贵的时间和更广泛地为该项目所做的工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-16
    • 1970-01-01
    • 2015-08-19
    • 2020-03-08
    • 2011-03-30
    • 1970-01-01
    相关资源
    最近更新 更多