PostgreSQL 16并行GROUP BY需满足四重条件:聚合函数支持Partial Mode(如string_agg无ORDER BY、array_agg、json_agg、sum/max/min/count非DISTINCT),数据量超min_parallel_table_scan_size,默认8MB,parallel_setup_cost和parallel_tuple_cost配置合理,且查询无volatile函数或FOR UPDATE等禁用并行操作;EXPLAIN (ANALYZE, VERBOSE)中出现Gather节点及其子节点Partial Aggregate才表明真正生效。

PostgreSQL 的 GROUP BY 本身不会自动并行,必须满足聚合函数、数据量、配置和执行计划四重条件,否则即使开了并行参数也只会走串行 HashAggregate 或 Sort + GroupAggregate。
哪些聚合函数能触发并行 GROUP BY
只有支持 Partial Mode 的聚合函数才可能参与并行拆分(partial → final 阶段)。PostgreSQL 16 明确支持的包括:
-
string_agg(col, ',')—— 但不能带ORDER BY子句,否则退化为串行 -
array_agg(col)—— 同样不支持ORDER BY,且输入为 NULL 时行为需确认 -
json_agg(col)和jsonb_agg(col)—— 无排序要求时可并行 -
sum()、max()、min()、count()—— 仅限非DISTINCT场景;count(distinct x)完全不支持并行 -
avg()在 PostgreSQL 16 中仍不支持 Partial Mode,所以无法并行
为什么 EXPLAIN 看不到 Gather 节点
即使设置了 max_parallel_workers_per_gather = 4,执行计划里仍可能没有 Gather 或 Partial Aggregate。常见原因有:
- 表实际扫描数据量低于
min_parallel_table_scan_size(默认 8MB),优化器直接跳过并行路径 -
parallel_setup_cost(默认 10)或parallel_tuple_cost(默认 0.1)过高,导致并行计划预估成本 > 串行 - 查询中含 volatile 函数(如
now()、random())、FOR UPDATE、或某些窗口函数,强制禁用并行 - GROUP BY 字段无索引,且数据未物理有序,优化器放弃并行 HashAggregate 改走 Sort + GroupAggregate
如何验证并行是否真正生效
别只看 EXPLAIN,要跑 EXPLAIN (ANALYZE, VERBOSE) 并检查输出结构:
- 出现
Gather或Gather Merge节点,且其子节点是Partial Aggregate,说明并行已启用 - 若只有
HashAggregate或GroupAggregate(无 Partial 前缀),就是串行 - 注意
Workers Launched字段值:为 0 表示根本没启动 worker;大于 0 才算真并行 - 对比
Actual Total Time:并行版本应明显低于串行,否则可能是数据倾斜或配置不合理
容易被忽略的性能陷阱
并行不是万能加速器,几个关键细节常被跳过:
- 数据倾斜会让并行变慢:如果 90% 的行落在同一个
user_id上,所有 worker 都得等那个最慢的归并,实测可能比单线程还慢 -
work_mem是每个 worker 独占的,不是全局共享——设成'64MB'且Workers Launched = 3,就额外消耗近 200MB 内存 - 分区表上并行效果更好,但前提是查询能命中单个或少数分区;跨全部分区扫描时,各分区并行 + 总 Gather 层叠,反而增加协调开销
-
GROUP BY + ORDER BY极易破坏并行:即使 ORDER BY 字段和 GROUP BY 完全一致,一旦聚合函数介入(如string_agg),优化器大概率放弃并行计划

















