Apache Cloudberry™ (Incubating) 是 Apache 软件基金会孵化项目,由 Greenplum 和 PostgreSQL 衍生而来,作为领先的开源 MPP 数据库,可用于建设企业级数据仓库,并适用于大规模分析和 AI/ML 工作负载。
GitHub: https://github.com/apache/cloudberry
在 Cloudberry 的实际性能优化中,我们始终聚焦一个核心目标:在执行 Join 时,尽早丢弃不可能匹配的无效行。在执行 Hash Join 时,传统执行路径需要完整扫描大表并对每行探测哈希表,浪费 CPU、内存和网络带宽。
而 Runtime Filter 的价值就在于,它并非纸上谈兵的优化建议,而是 在 Cloudberry 内部真正“可落地、可观测、持续生效”的加速能力。本文将聚焦 Runtime Filter 在 Cloudberry 中的落地实现细节。
Runtime Filter 实现
在执行 Hash Join 构建哈希表时,Cloudberry 会在内部同步生成 Bloom Filter 或 Range Filter:
Bloom Filter 通过哈希函数将小表的 Join Key 值映射到位数组,实现快速的概率过滤。内存消耗极小(通常仅需几 MB),但可能存在假阳性。 Range Filter 则记录 Join Key 的最小值和最大值,对于数值范围连续的数据(如时间戳、整型 ID)过滤效果更好。
这些过滤器在小表扫描时被无感知地构建,完全不需要额外扫描,也不需要二次计算,真正做到“顺手”完成。
下推至大表扫描节点
过滤器构建完成后,最关键的步骤是将它下推到大表的 SeqScan 节点,让过滤器在扫描时生效。
在 Cloudberry 中,Join 构建和大表扫描通常位于同一执行进程内,因此过滤器可以以内存指针的方式直接传递给大表扫描节点,避免了序列化和网络通信的额外成本。
在大表执行扫描时,每当拉取下一行数据时,系统会先将该行数据的 Join Key 列送入过滤器检查:
如果不在 Range Filter 范围内,直接丢弃。 如果 Bloom Filter 判断“不存在”,直接丢弃。 只有通过过滤的行,才会继续进入 Hash Join 参与探测。
这种在扫描时“预过滤”的模式,与 Cloudberry 的执行流水线完美适配,不会破坏流水线调度,也不会引入额外锁和同步延迟。
LOCAL 模式下推
业界的一些引擎会选择在跨节点环境中通过 GLOBAL 模式下推过滤器,将过滤器同步到所有数据节点,实现更大范围的预过滤。
在 Cloudberry 的第一阶段,我们刻意选择了 LOCAL 模式(进程内下推):
因为大部分 Broadcast Join 的场景,过滤器在进程内就足够高效; 避免了跨节点网络传输和序列化带来的延迟; 让过滤器的构建和应用零延迟生效,让收益最大化且稳定。
这种实现方式使 Runtime Filter 成为了 Cloudberry 查询链路中“真正无感知但持续生效”的能力。
在执行计划中可观测,让加速“看得见”
Runtime Filter 不仅仅是默默执行的幕后加速器,它在执行计划中是可被用户清晰感知的。当用户执行 EXPLAIN ANALYZE 时,可以看到类似如下输出:
Rows Removed by Pushdown Runtime Filter: 4,328,191
意味着有 430 万行在扫描时就被 Runtime Filter 丢弃了,不再进入 Hash Join 的计算管道。
这种“可见可观测”的设计对 DBA、性能调优工程师非常友好:
便于判断 Runtime Filter 是否生效; 能验证过滤效果是否达到预期; 为后续优化 SQL 提供直观依据。
代码中的“真实细节”
在 Cloudberry 的执行器中,Runtime Filter 并非独立流程,而是通过核心结构 AttrFilter 与 Hash Join 和 SeqScan 深度集成。
AttrFilter 在执行时记录:
Join 键范围(min/max)用于 Range Filter; Bloom Filter 实例用于概率过滤; Join 键位置映射(rattno/lattno)确保列正确匹配; 关联到目标 SeqScan 节点的 PlanState 指针,用于精确下推。
构建过程完全与 Hash Join 的 MultiExecPrivateHash 流程同步:
在小表哈希表构建时调用 AddTupleValuesIntoRF 将值写入 Bloom Filter 或更新范围; 构建完成后调用 PushdownRuntimeFilter 下推过滤器到目标扫描节点; 在查询结束时自动调用 FreeRuntimeFilter 回收内存,保证系统稳定性和内存安全。
这种嵌入式实现方式,使 Runtime Filter 成为了 Cloudberry 查询执行过程中“天然存在”的优化能力。
结语
在 Cloudberry,我们希望大部分优化能力都能做到“对用户无感,对系统有益”,Runtime Filter 正是这样一种能力。
它不需要用户额外学习参数,不需要写复杂 SQL Hint,也不需要在执行前进行特别配置,但只要你的查询包含 Join,它就会自动工作,为你节省时间与资源。
Runtime Filter 的使命非常纯粹: 在 Join 前,让不可能命中的行在最便宜的阶段被提前过滤掉,让资源只用于真正有价值的计算。
未来,我们将继续扩展 Runtime Filter:
在合适的场景中引入 GLOBAL 模式支持,跨节点做全局预过滤; 支持 IndexScan / BitmapScan 下推; 提供更加智能的过滤器精度控制; 实现与自适应并行度、管道执行更深度融合。
但无论演化到何种程度,这项能力的本质始终不变: 用最简单的方法,让 Cloudberry 更快、更稳、更省。
如果你想了解更多 Cloudberry 在执行链路中的底层优化实践,欢迎继续关注,我们会持续发布更多底层设计与优化实战分享。
👇🏻️扫码加入 Apache Cloudberry 交流群👇🏻️





