postgresql 16并行聚合需满足函数支持partial mode、数据量超阈值、无volatile函数等条件;手动调优参数如max_parallel_workers_per_gather和min_parallel_table_scan_size,并确保explain中出现gather节点方可生效。

PostgreSQL 16 的并行查询不会自动加速任意 GROUP BY 或聚合语句——它只对满足特定条件的查询生效,且必须手动调优参数才能触发。盲目套用并行配置反而可能因调度开销拖慢小表查询。
为什么 EXPLAIN 看不到 Gather 节点?
即使启用了并行参数,EXPLAIN ANALYZE 仍显示 GroupAggregate 而非 Gather + Partial Aggregate,常见原因包括:
- 聚合函数不支持 Partial Mode:例如
count(distinct col)、string_agg(col, ',' ORDER BY x)、avg(col)(16 之前版本)会直接禁用并行 - 扫描数据量低于阈值:
min_parallel_table_scan_size默认为 8MB,若实际扫描远小于此(如几万行小表),优化器跳过并行路径 - 存在 volatile 函数:
string_agg(now()::text, ',')或random()等调用会让整个计划退化为串行 - 查询含隐式禁止操作:如
FOR UPDATE、某些窗口函数、或在 plpgsql 函数中使用PERFORM/SELECT INTO中间赋值
哪些聚合写法真能并行?
只有满足「可分片 + 可合并」条件的组合才可能触发并行。关键看聚合函数是否实现 combine/serial/deserial 接口:
-
string_agg(col, ',')—— 必须无ORDER BY;带排序即失效 -
array_agg(col)、jsonb_agg(col)、json_agg(col)—— 同样禁止ORDER BY -
sum(col)、max(col)、min(col)、count(*)—— 支持;但count(col)(非星号)和count(distinct col)不支持 -
GROUP BY字段本身不能含 volatile 表达式,例如GROUP BY date_trunc('day', now())会阻断并行
如何让并行真正生效?
必须在查询执行前设置 session 级参数,推荐在函数内用 SET LOCAL 隔离作用域:
- 设 worker 数:
SET LOCAL max_parallel_workers_per_gather = 4;(建议 ≤ CPU 核心数) - 调低门槛:
SET LOCAL min_parallel_table_scan_size = 1MB;(仅测试用;生产按实际表大小设,如 100MB) - 压低成本估算:
SET LOCAL parallel_setup_cost = 2;和SET LOCAL parallel_tuple_cost = 0.01; - 确保内存充足:
SET LOCAL work_mem = '64MB';(总内存 ≈ (1 + workers) × work_mem) - 验证是否启用:
EXPLAIN (ANALYZE, VERBOSE)输出中出现Gather节点,且子节点含Partial Aggregate
在 plpgsql 函数里怎么写才不破坏并行?
函数封装本身不阻止并行,但常见写法会意外关闭它:
- 避免
RETURN QUERY SELECT ... GROUP BY ...中嵌套 volatile 表达式或ORDER BY子句 - 不要用
FOR rec IN SELECT ...或SELECT ... INTO var包裹聚合查询——这会触发 planner early-exit,跳过并行候选路径 - 如果必须用中间变量,改用
WITHCTE 提前物化,再对其聚合(CTE 本身不阻断并行) -
SET LOCAL参数在函数内有效,但若函数内开了BEGIN ... END事务块,这些设置仍持续到块结束
最容易被忽略的是:并行与否完全由查询计划决定,跟是不是在函数里、有没有加 PARALLEL SAFE 属性无关;哪怕所有参数都设对了,一行 ORDER BY 或一个 now() 就能让整个并行计划消失。











