并行查询最早是在PostgreSQL 9.6中引入的,此功能自那时以来已得到扩展。在PostgreSQL 11和PostgreSQL 12中,数据库引擎添加了更多功能。但是,仍有一些并行查询的问题经常出现,这绝对值得一些澄清。
估计顺序扫描的成本
为了向您展示该过程的工作原理,我创建了一个仅包含两列的简单表:
test=# CREATE TABLE t_test AS SELECT id AS many, id % 2 AS few FROM generate_series(1, 10000000) AS id;SELECT 10000000 test=# ANALYZE;ANALYZE
“many”列包含1000万个不同的条目。“few”列将包含两个不同的值。但是,出于这个示例的考虑,所有合理的大表都可以使用。
我们来看看PostgreSQL优化器是如何工作的:
test=# SET max_parallel_workers_per_gather TO 0;SETtest=# explain SELECT count(*) FROM t_test; QUERY PLAN------------------------------------------------------------------------ Aggregate (cost=169248.60..169248.61 rows=1 width=8) -> Seq Scan on t_test (cost=0.00..144248.48 rows=10000048 width=0) (2 rows)
默认配置将自动使PostgreSQL进行并行顺序扫描。我们希望阻止它这样做以使内容更易于理解。
可以通过将max_parallel_workers_per_gather变量设置为0来关闭并行查询。如在执行计划中所看到的,顺序扫描的成本估计为144248。总查询成本预计为169248。但是PostgreSQL如何得出这个数字?让我们看一下以下清单:
test=# SELECT pg_relation_size('t_test') AS size, pg_relation_size('t_test') 8192 AS blocks; size | blocks-----------+-------- 362479616 | 44248(1 row)
t_test表约为350 MB,由44248个块组成。每个块必须顺序读取和处理。必须对那些块中的所有行进行计数才能得出最终结果。优化器将使用以下公式估算成本:
test=# SELECT current_setting('seq_page_cost')::numeric * 44248 + current_setting('cpu_tuple_cost')::numeric * 10000000 + current_setting('cpu_operator_cost')::numeric * 10000000; ?column?------------- 169248.0000(1 row)
正如你所看到的,几个参数,这里正在使用:seq_page_cost告诉优化的阅读顺序块的成本。最重要的是,我们必须考虑到所有这些行都必须经过CPU(cpu_tuple_cost)才能最终计数。使用cpu_operator_cost是因为计数基本上与为每一行调用“ +1”相同。因此,顺序扫描的总成本为169248,这正是我们在计划中看到的。
估计并行顺序扫描
PostgreSQL的估计顺序扫描的方式往往有点模糊。因此,让我们看一下执行计划,看看会发生什么:
test=# SET max_parallel_workers_per_gather TO default;SETtest=# explain SELECT count(*) FROM t_test; QUERY PLAN------------------------------------------------------------------------------------ Finalize Aggregate (cost=97331.80..97331.81 rows=1 width=8) -> Gather (cost=97331.58..97331.79 rows=2 width=8) Workers Planned: 2 -> Partial Aggregate (cost=96331.58..96331.59 rows=1 width=8) -> Parallel Seq Scan on t_test (cost=0.00..85914.87 rows=4166687 width=0)(5 rows)
如您所见,PostgreSQL决定使用两颗CPU。但是PostgreSQL 是如何提出这一部分的呢?“行= 4166687”
答案在于以下公式:
10000048.0 (2 + (1 – 0.3 * 2)) = 4166686.66 rows
10000048行是PostgreSQL期望在表中的行数( 由ANALYZE之前确定)。接下来的事情是,PostgreSQL尝试去 确定有多少工作都必须由一个核心来完成。但是,公式 实际上是什么意思?
estimate = estimated_rows / (number_of_cores + (1 – leader_contribution * number_of_cores)
leader进程通常花费大量精力来计算结果。但是,我们假设leader将约30%的时间用于服务工作进程。因此,随着 核心数量的增加,leader的贡献将下降。如果有4个或更多的内核在工作,则leader将不再对扫描做出有意义的贡献-因此PostgreSQL将仅根据内核数来计算表的大小,而不是使用刚刚显示的公式。
其他并行操作
其他并行操作将使用类似的除数来估计。位图扫描等也是相同工作方式。
本文翻译自:https://www.cybertec-postgresql.com/en/how-postgresql-estimates-parallel-queries/




