看到 max_parallel_workers_per_gather = 2,很容易以为每条并行查询都会启动两个 worker;看到 max_parallel_workers = 8,又容易把它当成八个查询席位。这两个名字都比实际行为更直白。理解并行查询,关键是把规划时申请的数量和执行时成功启动的数量分开。
每个 Gather 的上限,不是固定配额
max_parallel_workers_per_gather 限制规划器能为一个 Gather 或 Gather Merge 节点规划多少个并行 worker。设为 2,不代表规划器一定选择 2:表大小、统计信息和成本估算都可能让它选择更少,甚至选择非并行计划。
执行时还有一道关:即使计划写着 Workers Planned: 2,当时也未必有两个 worker 可用。因此,诊断并行查询时不要只看配置值,要同时看 EXPLAIN (ANALYZE) 中的 Workers Planned 和 Workers Launched。
集群上限也不等于预留资源
max_parallel_workers 是并行 worker 的集群级上限,而不是给某条查询预留的名额。多个查询会竞争可用 worker;并行 worker 还受 max_worker_processes 所提供的后台进程容量约束。把单个 Gather 的上限调高,并不能保证执行时拿到更多 worker。
这也解释了一个常见现象:同一条 SQL 的计划没有变化,繁忙时却启动了更少的 worker。此时先检查资源竞争,不要立刻把问题归因于 SQL 或索引。
在测试库里对照计划与实际启动数
下面的命令适合在有建表权限的测试数据库运行。运行前把连接参数 PGDATABASE 等设为你的测试环境;脚本会创建约一百万行的测试表,最后删除它。不要在已有同名表的数据库中执行。
psql -X -v ON_ERROR_STOP=1 <<'SQL'
SHOW max_parallel_workers_per_gather;
SHOW max_parallel_workers;
SHOW max_worker_processes;
CREATE TABLE parallel_guc_demo AS
SELECT g AS id FROM generate_series(1, 1000000) AS g;
ANALYZE parallel_guc_demo;
SET max_parallel_workers_per_gather = 2;
SET min_parallel_table_scan_size = 0;
SET parallel_setup_cost = 0;
SET parallel_tuple_cost = 0;
EXPLAIN (ANALYZE, VERBOSE, COSTS OFF)
SELECT count(*) FROM parallel_guc_demo;
DROP TABLE parallel_guc_demo;
SQL
这里将几个规划成本调低,是为了更容易观察并行计划,不是生产环境的推荐配置。如果输出中出现 Gather,就对照 Workers Planned 与 Workers Launched;如果没有出现,也不要把 SET 误认为强制并行——规划器仍会根据查询和环境作决定。
调参时先问三个问题
- 计划申请了多少?看
Workers Planned,判断max_parallel_workers_per_gather是否真是当前瓶颈。 - 实际启动了多少?看
Workers Launched;低于计划值时,检查并发负载与全局 worker 容量。 - 查询真的更快了吗?并行有启动和数据汇总成本。比较真实业务 SQL 的耗时与资源消耗,再决定是否提高上限。
把这两个 GUC 当成边界,而不是承诺,才能避免“参数翻倍、性能却没变”的误判。