SQL parallel_workers 的表级并行设置与全局 max_parallel_workers 的协调

12次阅读

parallel_workers = 0 是硬性禁止并行,直接跳过并行计划生成;max_parallel_workers_per_gather 才是单查询并行度上限,parallel_workers 仅为意向值,最终取三者最小值。

SQL parallel_workers 的表级并行设置与全局 max_parallel_workers 的协调

parallel_workers 设为 0 是真禁用并行,不是“忽略”

很多人以为 parallel_workers = 0 只是“不主动启用”,其实它是硬性禁止:PostgreSQL 遇到这个设置会直接跳过并行计划生成,哪怕查询本身很重、max_parallel_workers 也足够。它比 force_parallel_mode = off 更底层,作用在表扫描阶段就截断了。

实操建议:

  • 调试时想彻底排除并行干扰,设 parallel_workers = 0 比调全局参数更精准
  • 生产中慎用,尤其对大分区表——某些场景下强制串行反而让单核跑满、拖慢整体响应
  • 注意它只影响该表的顺序扫描(Seq Scan)和位图堆扫描(Bitmap Heap Scan),索引扫描不受控

max_parallel_workers 是全局闸门,但不保证每个查询都分到额度

max_parallel_workers 是整个实例能同时运行的 worker 进程上限,不是“每查询可用数”。当多个查询并发执行,它们共享这个池子;一个查询申请 4 个 worker,另一个可能只能分到 1 个,甚至 0 个——取决于当时剩余配额和查询代价估算。

常见错误现象:

  • 单个大查询没走并行,查 EXPLAIN 显示 Workers Planned: 0,但 max_parallel_workers 明明设了 8 → 实际是其他后台任务(如 VACUUM、后台 worker)占满了额度
  • 提高 max_parallel_workers 后性能没提升,反而出现内存 OOM → 并行 worker 共享 work_mem,总数翻倍意味着总内存消耗可能翻几倍

表级 parallel_workers 超过 max_parallel_workers_per_gather 会被截断

PostgreSQL 真正限制单个查询并行度的是 max_parallel_workers_per_gather(默认 2),不是 max_parallel_workers。即使你给某张表设了 parallel_workers = 8,只要 max_parallel_workers_per_gather = 2,该查询最多只用 2 个 worker。

关键点:

  • parallel_workers 是“意向值”,最终生效数取 min(表设置, max_parallel_workers_per_gather, 剩余全局额度)
  • 修改 max_parallel_workers_per_gather 需要重启(9.6–13)或 reload(14+),但改表级 parallel_workers 可在线执行 ALTER TABLE …… SET (parallel_workers = N)
  • 对小表设高值(如 4)没意义——优化器会因代价低自动降为 0,不会真启 worker

并行度实际生效要看执行计划里的 Workers Launched

EXPLAIN (ANALYZE) 输出里 Workers Launched: 2 才代表真起了并行,光看 Workers Planned: 2 不够。后者只是优化器预估,可能因 runtime 资源不足、锁冲突、函数不可并行(比如含 random() 或未标记 PARALLEL SAFE 的 UDF)而 fallback。

容易被忽略的地方:

  • 某些内置函数(如 pg_sleep()txid_current())默认不是 PARALLEL SAFE,一用就让整个 plan 退化为串行
  • 分区表下,如果父表没设 parallel_workers,而子表设了,PostgreSQL 9.6–12 不继承,13+ 才支持继承(需显式 SET 到父表)
  • 并行 worker 不共享 session 设置,比如你在会话里 SET work_mem = '512MB',worker 仍用配置文件里的默认值

调参这事,盯着 Workers Launched 比盯着配置项数字管用得多。数值飘忽不定,往往不是配少了,而是某处隐式阻断了并行路径。

text=ZqhQzanResources