PostgreSQL 16 实现窗口函数并行执行与 RANGE 帧性能优化:支持 Parallel WindowAgg/Sort 节点、引入值分组跳跃算法,并增强帧类型提示与内存超限报错。

PostgreSQL 16 的窗口函数不是“加了几个新函数”,而是底层执行逻辑和并行能力的实质性升级——它让原本卡在单核、内存受限、排序瓶颈上的查询,真正跑起来。
窗口函数现在能并行执行了
过去 ROW_NUMBER()、SUM() OVER () 这类操作只能串行处理,哪怕你开了 max_parallel_workers_per_gather = 8,也毫无作用。PG 16 首次支持窗口函数的并行化,前提是满足以下条件:
- 窗口定义不含
UNBOUNDED PRECEDING这类无法分片的帧(比如ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW仍无法并行) - 分区键(
PARTITION BY)有对应索引,且数据分布较均匀(避免某一分区独占全部工作) -
work_mem足够支撑每个 worker 的局部排序,否则 fallback 到串行 + 外部归并
实操时可加 EXPLAIN (ANALYZE) 观察执行计划中是否出现 Parallel WindowAgg 或 Parallel Sort 节点;若只有 WindowAgg,说明并行被跳过。
RANGE 帧的性能大幅改善
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 在 PG 15 及之前版本极易触发全分区扫描+重复比较,尤其当 ORDER BY 字段存在大量重复值时。PG 16 引入了优化的 RANGE 查找算法:
- 对已排序输入自动做“值分组跳跃”,跳过相同
ORDER BY值的重复计算 - 结合
pg_stat_progress_sort.sort_bytes可更准判断是否落盘——PG 16 中该视图对 RANGE 窗口同样有效 - 若业务真需
RANGE语义(如按时间区间累计),优先确保ORDER BY列有索引,且避免用表达式(如date_trunc('day', ts))直接排序,改用函数索引
默认帧行为更可控,但别依赖它
PG 16 没改变 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 仍是默认帧的事实,但它强化了对“失控帧”的预警能力:
-
EXPLAIN ANALYZE中,WindowAgg节点会明确标注Frame Type: Rows或Frame Type: Range,方便快速识别语义 - 当帧范围导致内存超限时,错误信息从模糊的 “out of memory” 改为更具体的 “window frame requires more than
work_mem” - 开发阶段建议显式写清帧定义:哪怕业务真要累计,也写成
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,而不是省略——省略容易掩盖索引缺失或数据倾斜问题
真正影响性能的从来不是函数名本身,而是 PARTITION BY 和 ORDER BY 如何与索引、内存、并行度咬合。PG 16 把这些咬合点变得更紧,但也更不容错——建错一个索引顺序,或漏调一个 work_mem,并行就自动退化,而你可能只看到执行时间从 2s 变成 1.8s,误以为“没提升”。

















