前几天读到一篇关于"先groupby再JOIN"的数据库论文:
没想到今天在PG社区就看到类似场景的优化补丁了: Implement Eager Aggregation
这个PATCH 由 Antonin Houska 在 2017 年提出原型. 2025 Richard Guo(郭峰@OpenPie) 重构并重写了大部分代码,解决了正确性、性能和集成问题。经过多轮社区评审(Robert Haas、Tom Lane 等核心开发者参与),最终合并。
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=8e11859102f947e6145acdd809e5cdcdfbe90fa5
下面来看一下这个patch的详细解读 :
这个 PostgreSQL 补丁 “Implement Eager Aggregation”(实现“急切聚合”)由 Richard Guo 提交,是对查询优化器的一项重要增强。它引入了一种称为 Eager Aggregation(急切聚合) 的优化技术,旨在通过在连接(JOIN)之前提前执行部分聚合,减少中间数据量,从而提升查询性能。
下面从多个维度详细讲解该补丁的核心思想、实现机制、限制条件和意义。
Eager Aggregation 是一种查询优化策略:
在执行 JOIN 之前,尽可能早地对某个表或子查询进行部分聚合(partial aggregation),然后再参与后续的连接操作,最终在顶层完成完整的聚合。
原始查询:
SELECT a.x, SUM(b.y)
FROM a
JOIN b ON a.id = b.a_id
GROUP BY a.x;
传统执行计划:
a 和 b 做完整连接(可能产生大量中间行);a.x 分组并聚合 SUM(b.y)。启用 Eager Aggregation 后的优化计划:
b 按 b.a_id 做部分聚合(例如 SUM(b.y) GROUP BY b.a_id);b 与 a 连接;a.x 做最终聚合(可能只需重分组,无需再处理大量原始行)。效果:如果 b 中每个 a_id 对应大量行,提前聚合可显著减少连接输入规模。
PostgreSQL 原有的优化器架构存在一个结构性限制:
扫描/连接规划阶段(scan/join planning)与聚合阶段是分离的。 聚合信息(如 GROUP BY、HAVING、聚合函数)在构建连接树时不可见,因此无法在连接前考虑是否可以提前聚合。
这导致优化器错失了通过提前聚合减少中间数据量的机会。
Eager Aggregation 通过在规划早期收集聚合上下文,并在连接规划阶段动态生成“已分组的关系(grouped relation)”,打破了这一限制。
PlannerInfo 中记录:这些信息在后续的连接规划中用于判断是否可应用 Eager Aggregation。
RelOptInfo,称为 grouped relation。为控制规划开销,只对最便宜的或已排序的非分组路径生成分组路径。
当对某个表做部分聚合时,必须将该表中用于后续 JOIN 的所有列加入分组键。
例如:
... FROM a JOIN b ON a.id = b.a_id AND a.status = b.status ...
若对 b 做部分聚合,则分组键必须包含 b.a_id 和 b.status,否则:
a 行;补丁通过分析上层 JOIN 条件,自动将相关列加入 partial GROUP BY。
✅ 性能提升:对“大表连接后聚合”的场景(如星型模型、OLAP 查询)有显著加速。 ✅ 架构改进:打破 scan/join 与 aggregation 的隔离,为未来更多聚合感知优化铺路。 ⚠️ 谨慎启用:通过 cost 模型自动决策,无需用户干预,保证安全性和通用性。
特性 | 说明 |
|---|---|
目标 | 减少 JOIN 前的数据量,提升聚合查询性能 |
核心机制 | 在连接规划阶段生成“部分聚合”的 grouped relation |
关键保障 | 扩展分组键以保持 JOIN 语义正确;禁止用于 OUTER JOIN nullable 侧 |
规划开销控制 | 仅在收益显著时应用;限制路径生成数量 |
用户透明 | 自动启用,无需语法或配置变更 |
这项补丁代表了 PostgreSQL 优化器向更智能、更上下文感知方向迈出的重要一步,尤其利好数据分析类负载。