存储过程不支持并行查询,因并行由sql语句的查询计划决定而非函数封装;仅当满足可分片+可合并条件、无volatile函数、正确设置parallel参数且explain显示gather与partial aggregate时才真正并行。

存储过程本身不支持并行查询——并行与否由执行它的 SQL 语句决定,不是由是否在 CREATE OR REPLACE FUNCTION 里包裹决定的。
为什么存储过程里写 SELECT 就不自动并行?
PostgreSQL 的并行能力作用于「查询计划层级」,不是「函数封装层级」。哪怕你在 plpgsql 函数里写了一个带 GROUP BY 的大查询,只要这个查询没被优化器选中并行路径,它就还是串行跑。
常见错误现象:EXPLAIN ANALYZE 看到的是 GroupAggregate 节点,没有 Gather 或 Partial Aggregate —— 这说明并行根本没启动,跟是不是在函数里无关。
- 函数调用本身会引入额外开销(如变量解析、控制流跳转),可能让优化器更倾向保守的串行计划
-
RETURN QUERY或FOR ... IN SELECT中的查询,仍按普通查询规则做代价估算 - 如果函数里用了
PERFORM或中间赋值(如SELECT ... INTO),还可能触发 planner 的 early-exit 逻辑,跳过并行候选路径
哪些 GROUP BY 查询在函数里才可能并行?
只有满足「可分片 + 可合并」条件的聚合,在函数内执行时才有机会走并行。关键看聚合函数和写法:
-
string_agg(col, ',')—— 必须不带ORDER BY;带了就退化为串行 -
array_agg(col)、jsonb_agg(col)—— 同样禁止ORDER BY子句 -
sum(col)、count(*)、max(col)—— 支持并行;但count(distinct col)不支持 - 所有聚合字段不能含 volatile 函数,比如
string_agg(now()::text, ',')会直接禁用并行
怎么让函数里的查询真正触发 Gather + Partial Aggregate?
必须手动干预参数,并且只对当前 session 或当前函数生效(推荐用 SET LOCAL):
- 设 worker 数:
SET LOCAL max_parallel_workers_per_gather = 4;(别超 CPU 核心数) - 压低门槛:
SET LOCAL min_parallel_table_scan_size = 1MB;(小表测试可用,生产建议按实际大小设) - 调低成本:
SET LOCAL parallel_setup_cost = 2;和SET LOCAL parallel_tuple_cost = 0.01; - 确保
work_mem足够:SET LOCAL work_mem = '64MB';(总内存 ≈ (1 + workers) × work_mem)
注意:SET LOCAL 只影响当前函数调用内的 SQL,退出函数即恢复;但如果函数里开了事务块(BEGIN ... END),这些设置仍有效。
最容易被忽略的点:函数体里不能有隐式禁止并行的操作
哪怕参数全调对了,以下写法也会让整个查询退回到串行:
- 在 SELECT 中调用
random()、now()、clock_timestamp()等 volatile 函数 - 用了
FOR UPDATE或SELECT ... INTO+ 锁定行(即使没显式写 FOR UPDATE,某些隔离级别下也隐含) - 聚合字段上套了表达式,比如
string_agg(lower(name), ',')—— lower 是 stable,但若 name 列含 NULL,部分版本会绕过 partial path - 函数声明为
VOLATILE(默认行为),而内部查询又依赖 session 设置 —— 建议显式声明为STABLE,避免 planner 过度保守
真正起效的信号只有一个:EXPLAIN (ANALYZE, VERBOSE) 输出里出现 Gather 节点,且其子节点明确标着 Partial Aggregate。其他都是假象。











