了解查询中的 COUNT 行为、EXPLAIN 与函数
Understanding COUNT behavior in queries vs. EXPLAIN vs. functions
我很想了解(并可能改进)我在使用 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 慢?而且慢很多!是不是没有利用parallel workers,如果没有,为什么不呢?
开始编辑
按照下面的回答中的建议,我加载了 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 个 worker 却启动了 none。其他服务器空闲,配置相同,查询相同
编辑结束
好的。那么如果我这样做呢:
db=> SELECT COUNT(*) FROM mv_pks();
count
---------
2567883
(1 row)
Time: 72.687 ms
与不使用 EXPLAIN ANALYZE
直接在实体化视图上计算行数的性能相同,但你必须在这里相信我:此函数的性能取决于函数执行时实体化视图的状态被建造。这里的快速计时是在 table 为空时创建函数的结果。如果我在 table 已满时重新创建该函数,该函数需要超过 1000 毫秒才能 运行!
总结一下我的问题:
- 为什么
STABLE
SQL 函数内的 SQL 查询不带参数比该函数外的查询慢得多。
- 为什么 SQL 查询在使用
EXPLAIN ANALYZE
时慢得多?
- 为什么我在计算一个函数的行数时会得到所有不同的结果,该函数可以与计算实体化视图上的行数的速度相当快,或者比任何其他方法都慢,具体取决于函数的创建时间?
提前致谢!
对于1),你可以使用auto_explain
找到自己,它可以显示函数内部查询的计划。它使用并行计划吗?
对于2),这是测量的开销,这取决于平台,但可能很高。
对于 3) 比较两种情况下的 SQL 计划。 SQL 函数中的查询没有被缓存,所以我没有解释为什么它会这样。您是否多次重复测试以排除您看到从磁盘读取与从缓存读取的效果?
我很想了解(并可能改进)我在使用 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 慢?而且慢很多!是不是没有利用parallel workers,如果没有,为什么不呢?
开始编辑
按照下面的回答中的建议,我加载了 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 个 worker 却启动了 none。其他服务器空闲,配置相同,查询相同
编辑结束
好的。那么如果我这样做呢:
db=> SELECT COUNT(*) FROM mv_pks();
count
---------
2567883
(1 row)
Time: 72.687 ms
与不使用 EXPLAIN ANALYZE
直接在实体化视图上计算行数的性能相同,但你必须在这里相信我:此函数的性能取决于函数执行时实体化视图的状态被建造。这里的快速计时是在 table 为空时创建函数的结果。如果我在 table 已满时重新创建该函数,该函数需要超过 1000 毫秒才能 运行!
总结一下我的问题:
- 为什么
STABLE
SQL 函数内的 SQL 查询不带参数比该函数外的查询慢得多。 - 为什么 SQL 查询在使用
EXPLAIN ANALYZE
时慢得多? - 为什么我在计算一个函数的行数时会得到所有不同的结果,该函数可以与计算实体化视图上的行数的速度相当快,或者比任何其他方法都慢,具体取决于函数的创建时间?
提前致谢!
对于1),你可以使用auto_explain
找到自己,它可以显示函数内部查询的计划。它使用并行计划吗?
对于2),这是测量的开销,这取决于平台,但可能很高。
对于 3) 比较两种情况下的 SQL 计划。 SQL 函数中的查询没有被缓存,所以我没有解释为什么它会这样。您是否多次重复测试以排除您看到从磁盘读取与从缓存读取的效果?