postgresql的并行scan是数据库在处理大表查询、复杂聚合等场景时,通过启动多个工作进程共同完成数据扫描任务来提升查询效率的核心机制,它的运作依赖一套完整的进程调度、任务拆分和结果汇总逻辑,是postgresql并行查询能力的重要组成部分。

postgresql并行scan的核心参与角色
并行scan的运作涉及多个关键角色,各角色分工明确共同完成扫描任务:
- 领导进程(Leader Process):也就是执行查询的原始后端进程,负责生成并行查询计划,启动并行worker进程,同时也会参与部分扫描工作,最后汇总所有worker返回的结果。
- 并行worker进程(Parallel Worker):由领导进程启动的后台工作进程,负责执行分配到的部分扫描任务,每个worker独立工作,互不干扰。
- 动态共享内存(Dynamic Shared Memory):用于存储并行查询的计划信息、任务分配状态、中间结果等共享数据,保证领导进程和所有worker可以访问到一致的上下文信息。
并行scan的完整工作流程
postgresql并行scan的运作可以分为计划生成、进程启动、任务执行、结果汇总四个阶段:
1. 并行查询计划生成阶段
当postgresql接收到一条查询语句后,优化器会先判断该查询是否适合并行执行。判断条件包括表的数据量是否达到并行扫描阈值、查询是否包含不支持并行的操作(比如某些窗口函数、修改操作等)。如果符合并行条件,优化器会生成包含并行scan节点的执行计划,同时确定需要启动的并行worker数量,数量由max_parallel_workers_per_gather参数控制,默认是2。
2. 并行进程启动阶段
执行器开始运行并行计划时,领导进程会先申请动态共享内存区域,将并行计划的详细信息、扫描任务的初始状态写入共享内存,然后根据计划启动对应数量的并行worker进程。worker进程启动后会读取共享内存中的计划信息,完成自身的初始化工作。
3. 扫描任务执行阶段
并行scan的任务拆分方式根据扫描类型有所不同:
- 并行顺序扫描(Parallel Sequential Scan):领导进程会将表的块范围拆分成多个小的块区间,每个worker领取一个区间进行扫描,扫描过程中如果某个worker完成了自己的任务,还可以继续领取未被分配的新区间,避免负载不均。
- 并行索引扫描(Parallel Index Scan):对于B树索引等支持的索引类型,会将索引的叶子页范围拆分成多个区间,每个worker负责扫描对应区间的索引项,再根据索引项回表获取对应的数据行。
每个worker在扫描过程中会独立过滤符合条件的数据行,将中间结果写入共享内存中的结果队列,或者暂时缓存在本地内存中。
4. 结果汇总阶段
所有worker完成分配的扫描任务后,领导进程会收集所有worker返回的中间结果,进行最终的聚合、排序、过滤等操作,得到最终的查询结果返回给客户端。如果查询包含聚合函数,postgresql还支持部分聚合下推,每个worker先完成本地数据的部分聚合,再返回给领导进程做最终聚合,减少数据传输量。
并行机制加速业务的原理
并行scan之所以能加速查询,核心原因是把原本单个进程串行完成的扫描任务拆分给多个进程并行处理,充分利用多核CPU的计算能力:
- 对于大表全表扫描场景,单个进程扫描可能需要数秒甚至数十秒,启动4个并行worker后,理论上扫描时间可以缩短到原来的四分之一左右,大幅降低查询响应时间。
- 对于包含聚合操作的查询,部分聚合下推减少了领导进程需要处理的数据量,同时多个worker并行计算聚合值,进一步提升计算效率。
- 并行机制可以和postgresql的其他优化手段(比如索引优化、分区表)结合使用,在分区表场景下每个分区可以独立触发并行scan,进一步提升大规模分区表的查询效率。
并行scan的相关配置参数
要合理使用并行scan,需要关注以下几个核心配置参数:
| 参数名 | 默认值 | 作用说明 |
|---|---|---|
| max_parallel_workers_per_gather | 2 | 单个Gather节点最多可以启动的并行worker数量,也就是单次并行scan的最大worker数 |
| max_parallel_workers | 8 | 整个数据库实例同时可以存在的并行worker总数量,避免并行进程过多占用过多系统资源 |
| min_parallel_table_scan_size | 8MB | 触发并行顺序扫描的最小表大小,小于该值的表不会使用并行顺序扫描 |
| min_parallel_index_scan_size | 512KB | 触发并行索引扫描的最小索引大小,小于该值的索引不会使用并行索引扫描 |
并行scan的使用限制
并不是所有场景都适合使用并行scan,以下情况并行scan不会生效:
- 查询涉及的数据表大小小于并行扫描的最小阈值。
- 查询包含不支持并行的操作,比如使用游标、某些复杂的窗口函数、临时表的扫描等。
- 当前数据库的并行worker总数已经达到
max_parallel_workers的上限,无法再启动新的worker。 - 查询是简单的点查,走索引直接返回少量数据,并行scan的进程启动开销反而会降低查询效率。
并行scan示例
我们可以通过一个简单的大表聚合查询来观察并行scan的效果,首先创建一张测试大表并插入数据:
-- 创建测试表
CREATE TABLE test_large_table (
id SERIAL PRIMARY KEY,
user_id INT,
score INT,
create_time TIMESTAMP
);
-- 插入1000万条测试数据
INSERT INTO test_large_table (user_id, score, create_time)
SELECT
floor(random() * 10000)::INT,
floor(random() * 100)::INT,
now() - (random() * interval '1 year')
FROM generate_series(1, 10000000);
然后执行聚合查询,开启并行后的执行计划如下:
-- 开启并行执行计划查看 EXPLAIN ANALYZE SELECT avg(score) FROM test_large_table WHERE user_id < 5000;
执行计划中会显示Gather节点,下面挂载多个Parallel Seq Scan节点,说明启用了并行scan,对比关闭并行后的执行时间,可以明显看到并行scan带来的性能提升。
如果要临时关闭并行scan测试对比效果,可以执行以下语句:
-- 临时关闭当前会话的并行scan SET max_parallel_workers_per_gather = 0; -- 再次执行查询,此时会使用串行扫描 EXPLAIN ANALYZE SELECT avg(score) FROM test_large_table WHERE user_id < 5000;
postgresql并行scan并行查询数据库优化并行业务加速修改时间:2026-07-05 02:57:32