max_parallel_workers_per_gather设为cpu物理核数的一半通常是安全起点,但需结合olap/oltp场景、全局max_parallel_workers上限、查询并行兼容性及资源开销综合调整,避免盲目调高导致性能下降。

max_parallel_workers_per_gather 设多少才不浪费 CPU
这个参数不是设得越高越好,也不是直接等于 CPU 核数就对。postgresql 的并行 worker 是按查询动态申请的,实际能跑满多少,取决于查询结构、数据分布、内存和锁竞争,而不是单纯看 CPU 有多少空闲核。
实操建议:
-
max_parallel_workers_per_gather默认是 2,对多数 OLTP 场景够用;OLAP 或大表扫描才需要调高 - 单个查询最多只能用
min(设置值, max_parallel_workers)个 worker,而max_parallel_workers是全局上限(默认 8),它必须 ≥ 前者,否则调了也没用 - 设成 CPU 物理核数的一半通常是安全起点(比如 16 核 → 设 8),但若服务器还跑其他服务(如应用、备份),建议再打 7 折(→ 5~6)
- 超过 8 之后收益明显下降:worker 间协调开销、共享缓冲区争用、tuple 传输成本会快速抵消并行增益
为什么设了 16 却只看到 4 个 worker 在跑
常见错误现象:EXPLAIN (ANALYZE, BUFFERS) 显示 Workers Launched: 4,但 max_parallel_workers_per_gather = 16,以为配置没生效。
真实原因往往不是配置问题,而是查询本身不满足并行条件:
- 目标表没建
parallel_workers(用ALTER table t SET (parallel_workers = 4)手动指定才可能突破默认 0) - 用了不支持并行的算子,比如
NOT IN (subquery)、某些自定义函数、或GROUP BY含不可哈希类型(如jsonb) - 优化器估算行数太少(默认
parallel_threshold是 1000,低于它认为不值得并行) - 事务隔离级别是
SERIALIZABLE,或开启了jit = off(JIT 关闭时部分计划路径会退化)
调整后查询变慢?检查这几个隐性开销点
并行不是免费的。worker 多了,反而拖慢查询,通常是因为以下兼容性或资源错配:
- 每个 worker 都要分一份
work_mem,设成 64MB + 8 个 worker → 实际内存占用可能翻倍,触发 swap 或 OOM killer - shared_buffers 竞争加剧:多个 worker 同时读同一块 page,导致 buffer pin 等待(
BufferPinwait Event) - NUMA 架构下,worker 被调度到远端 node,跨 NUMA 访存延迟升高(
vmstat -s | grep "numa"可查分配倾向) - 顺序扫描虽并行了,但
random_page_cost没调低,优化器仍倾向走索引,结果并行根本没触发
生产环境怎么稳住这个参数
不要全局一刀切。不同业务查询负载差异大,硬设一个值容易顾此失彼:
- 用
ALTER SYSTEM SET max_parallel_workers_per_gather = 2保底,避免突发大查询打爆系统 - 对已知重查询,用
SET LOCAL max_parallel_workers_per_gather = 6在事务内临时提权(注意:仅在BEGIN后生效) - 监控
pg_stat_statements中calls和total_time,重点看max_parallel_workers_per_gather变更前后,哪些 query 的mean_time波动 >20% - 真正关键的是
parallel_leader_participation—— 设为off可让 leader 进程专注协调,避免它和 worker 争 CPU,这对高并发小查询更友好
最常被忽略的一点:这个参数只影响 gather 节点下的 worker 数量,不影响 index scan、hash join 内部的并行逻辑——那些由各自算子的 parallel_workers 设置控制,得单独调。