PostgreSQL 18 中文参考文档(未翻译部分保留英文)🌐 切换到英文原版 →

15.1. 并行查询的工作原理 #

当查询优化器判定并行查询是某条特定 SQL 的最高效执行策略时,它会生成一个包含 Gather(收集)或 Gather Merge(收集并合并排序)算子节点的执行计划树。一个基础示例如下:

EXPLAIN SELECT * FROM pgbench_accounts WHERE filler LIKE '%x%';
                                     QUERY PLAN
-------------------------------------------------------------------​------------------
 Gather  (cost=1000.00..217018.43 rows=1 width=97)
   Workers Planned: 2
   ->  Parallel Seq Scan on pgbench_accounts  (cost=0.00..216018.33 rows=1 width=97)
         Filter: (filler ~~ '%x%'::text)
(4 rows)

在所有并行计划中,Gather 或 Gather Merge 节点有且仅有一个子计划分支,该分支正是将被多进程并发执行的核心并行区间。如果 Gather 算子位于整个执行计划树的最顶层根节点,意味着整个查询在深层全盘并行执行;如果它位于计划树的中间某处,则仅有位于其下方的子树算子会并发执行。在上例中,查询仅访问单张表,因此除 Gather 本身外只有一个子节点,该子节点作为 Gather 的从属分支将在后台多工作进程中并发运行。

通过 使用 EXPLAIN,可以直观观察到优化器为该计划预设规划的工作进程数量。在查询实际开始执行并到达 Gather 节点时,负责维护用户当前会话的主进程(称为 leader 进程)会向系统申请唤起一组后台工作进程(background worker processes),其数量等于规划值。优化器所能申请的工作进程数受限于参数 max_parallel_workers_per_gather 的上限;而集群在任一时刻能够共存的后台工作进程总数则由 max_worker_processes 与 max_parallel_workers 共同硬性管控。因此,受制于实时运行环境的负载配额,并行查询在实际启动时获取的工作进程数可能少于规划数,极端情况下甚至可能完全获取不到任何后台工作进程(此时退化为由 leader 进程单核串行模拟执行)。若该现象频繁出现,建议调大 max_worker_processes 与 max_parallel_workers 允许系统调度更多并发进程,或调小 max_parallel_workers_per_gather 减少单次查询占用的进程数。

为当前查询成功拉起的每个后台工作进程(worker)都会独立执行并行区间的算子逻辑。主进程(leader)在调度协调的同时也会分担一部分并行计算,但它还承担了一项额外职责:负责持续读取并消费所有 worker 进程产出回传的数据元组。当并行算子产出的结果集规模较小时,leader 进程的表现与普通 worker 无异,全力分担计算加速处理;相反,若并行子树产出了海量数据行,leader 进程的大部分 CPU 算力将被用于从 IPC 共享内存队列中收取读取元组,并继续执行位于 Gather 节点上方的非并行顶层算子。在这种场景下,leader 进程分担底层并行扫描的精力将大幅减少。

当并行子树顶部的收集节点是 Gather Merge 而非普通 Gather 时,表明下方并发执行的各个 worker 进程所产出的数据行各自均已具备局部有序性,leader 进程此时负责执行多路保序归并。相反,普通 Gather 节点则以任意就绪次序贪婪接收各 worker 产出的元组,完全打破原有的物理顺序。