SQL max_parallel_workers_per_gather 的并行 worker 数量与 CPU 核数匹配经验

1次阅读

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

SQL max_parallel_workers_per_gather 的并行 worker 数量与 CPU 核数匹配经验

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 等待(BufferPin wait 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_statementscallstotal_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 设置控制,得单独调。

text=ZqhQzanResources