parallel_workers 安全起点为 min(4, (cpu核数−2)/2),需兼顾cpu核数、并发度与负载类型;设过高易致线程争抢和oom,应结合work_mem调优并以explain验证实际执行计划。

parallel_workers 设多少才不浪费 CPU
PostgreSQL 的 parallel_workers 不是设得越高越好,它受实际可用 CPU 核数、查询并发度和工作负载类型三重限制。盲目设成 32 或等于物理核数,反而可能触发大量线程争抢、上下文切换开销暴涨,查询变慢。
- 单个查询最多启动
parallel_workers + 1个进程(1 主 + N 工作进程),所以设8意味着单次并行扫描最多用 9 个 CPU 时间片 - 默认值为
0,表示“由优化器决定”,但多数场景下它过于保守(常只用 2 个 worker) - 安全起点:取
min(4, (CPU 核数 - 2) / 2)—— 留出 2 核给系统和其他连接,再对半分给并行任务 - 若服务器长期 CPU 使用率 6 或
8;一旦观察到pg_stat_activity.state = 'active'但wait_event = 'ClientRead'频繁出现,说明 worker 在等 I/O 或锁,再加没意义
如何在表级别覆盖全局 parallel_workers
全局 max_parallel_workers_per_gather 是总闸门,但真正起作用的是每个表的 parallel_workers 存储参数。没显式设置,优化器就按数据量估算,往往不准。
- 用
ALTER TABLE orders SET (parallel_workers = 4);直接生效,无需重启 - 该设置只影响后续对该表的顺序扫描(Seq Scan)、位图堆扫描(Bitmap Heap Scan)等支持并行的操作,索引扫描(Index Scan)默认不并行
- 若表有分区,需对每个子表单独设置;父表上的设置不会继承
- 执行
EXPLAIN (ANALYZE, BUFFERS) SELECT COUNT(*) FROM orders;后看输出里是否出现Gather节点及下面的Workers Launched数字,这是唯一验证方式
为什么开了 parallel_workers 却没走并行计划
常见错觉是“设了就一定并行”,其实 PostgreSQL 有一串硬性门槛,缺一不可。
-
max_parallel_workers_per_gather必须 > 0(全局配置,默认为 2) - 查询必须能使用顺序扫描或位图扫描——带高选择性条件的
WHERE id = ?几乎永远走索引扫描,不触发并行 - 目标表大小要超过
min_parallel_table_scan_size(默认 8MB),小表强制并行反而更慢 - 当前事务隔离级别不能是
serializable,该级别下并行被禁用 - 如果查询里含不支持并行的函数(如
pg_backend_pid()、current_user),整个计划降级为串行
并行 worker 吃光内存导致 OOM 怎么办
每个 worker 进程会独立申请 work_mem 内存,设 parallel_workers = 4 且 work_mem = 64MB,单个查询最高可能占用 (4 + 1) × 64MB = 320MB,多个大查询并发就容易触发 Linux OOM Killer 杀掉 backend。
- 优先调低
work_mem(比如从 64MB 改为 16MB),比减少parallel_workers更治本 - 监控
pg_stat_statements中max_exec_time和total_plan_time突增的查询,它们往往是并行失控的源头 - 用
pg_stat_progress_parallel视图实时看哪些查询正在用几个 worker、运行多久、是否卡在 barrier 等待 - 不要依赖
shared_buffers缓解——worker 进程不共享这块内存,它只用于缓存页,不影响 worker 内存分配
复杂点在于:同一张表,在 OLAP 大宽表聚合时需要高并行,在 OLTP 点查场景下又必须关闭并行。没有一刀切的数值,得靠 EXPLAIN 看真实执行计划,而不是凭核数拍脑袋。










