暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

给 DuckDB 的 JOIN 加了三层保险。VLDB 2026 的一篇论文

alitrack 6小时前
0

数据库优化器选错 JOIN 顺序是一件很常见的事。统计信息不准确、数据倾斜、复杂的过滤条件——任何一个都可能导致优化器选出一个次优计划,查询性能差几个数量级。

去年有一篇论文提出了一个思路叫 Robust Predicate Transfer(RPT):在 JOIN 执行过程中,把一侧过滤后的值集合做成 Bloom Filter,推到另一侧去提前筛掉不匹配的行。相当于你在 JOIN 之前先问对方:"我这边符合条件的 key 只有这些,你看你能不能用这个先滤一遍"。即使优化器选错了 JOIN 顺序,RPT 也能在运行时修正一部分性能损失。

思路很好,但实现上有坑。冗余的 Bloom Filter 构建、低效的数据扫描——RPT 在修正部分查询的同时,也拖慢了另外一些查询。

今年,三位作者——清华的乔一鸣、CWI 的 Peter Boncz(他也是 DuckDB 的创始人之一)和清华的张焕晨——在 VLDB 2026 上发了一篇论文,提出了 RPT+,三个改进把 RPT 的问题修掉了。

论文在 DuckDB v1.3.0 上实现,代码在 github.com/embryo-labs/dynamic-predicate-transfer 开源,MIT 协议。

● ● ●

RPT 的问题在哪

RPT 的核心机制不复杂:在多表 JOIN 中,每个表的过滤条件筛出一组 key,这些 key 做成 Bloom Filter,传递(transfer)到参与 JOIN 的其他表。收到 Filter 的表在扫描时就能跳过不匹配的 block,减少实际 JOIN 的数据量。

但这个过程有几个浪费:

冗余 Filter 构建。 如果三个表 A ⋈ B ⋈ C 都有 WHERE 过滤,RPT 会构造 6 个 Bloom Filter(两两传递),而实际上很多 Filter 是信息量重复的。A 传给 B 的 Filter 和 A 传给 C 的 Filter 可能内容一样,但各建了一次。

扫描效率低下。 RPT 的 Filter 只在 tuple 级别工作——扫一个 block,逐行查 Bloom Filter。它做不到 block-level skipping,如果一个 block 里的所有行都不匹配,还是要扫完才知道。

静态决策浪费资源。 RPT 在查询开始时决定建哪些 Filter,不管这个 Filter 最终有没有用。如果一个 Filter 的选择性很低(大部分 key 都在),建它是白费 CPU 和内存。

● ● ●

RPT+ 的三个改进

第一个改进:非对称传递计划(Asymmetric Transfer Plans)。 不再两两建 Filter。RPT+ 分析 query graph,找到一个最小覆盖集——只在真正需要传递信息的方向上建 Filter。还是 A ⋈ B ⋈ C 的查询,可能只需要建 2 个 Filter 而不是 6 个。

第二个改进:级联过滤器(Cascade Filters)。 一个 Filter 同时做两层筛选:block-level 的 approximate filter 先快速跳过整个 block,通过了再进 tuple-level 的精确 Bloom Filter。大部分数据在 block 层面就被截住了。

第三个改进:动态流水线(Dynamic Pipelines)。 Filter 不在一开始建好,而是在执行过程中动态决定是否值得建。RPT+ 在运行时监控 Filter 的选择性——如果发现一个 Filter 的选择性太差(大部分行都匹配),直接放弃构建,省下计算资源。这个决策在 query 执行中途实时做出。

三个改进的目标是一致的:只在对的地方建 Filter,只在有用的时候建 Filter,用最高效的方式用 Filter。

● ● ●

数据怎么说

论文在四个 benchmark 上测试:

BenchmarkRPT+ 加速比 (vs DuckDB)查询数说明
JOB**1.47×**113IMDb 数据集,真实查询负载
SQLStorm**1.28×**13,308StackOverflow,超大规模查询集
TPC-H**1.17×**22标准 OLAP benchmark
Appian**1.01×**~30企业应用负载,RPT 原版退步严重

更重要的是回归测试。JOB 上 RPT 原版有若干个查询比 DuckDB baseline 还慢,RPT+ 全部消除了。Appian 上 RPT 原版的退化被完全清零。SQLStorm 的 13,308 个查询中,退化也被大幅削减。

RPT+ 不是在"大多数时候变快"——它是在"不拖慢任何查询"的前提下做到了提速。这是论文题目里 "Robust"(鲁棒)的来源。

● ● ●

跟 DuckDB 最近的优化器改进放在一起看

这两个月 DuckDB 在优化器上下了不少功夫。上个月合入的 Eager Aggregation(PR #23506,基于 1995 年 Yan & Larson 论文)把 GROUP BY 推到 JOIN 前面,特定场景快了 622 倍。RPT+ 解决的是另一个维度的问题:JOIN 顺序错了怎么办。

一个是重排执行顺序,一个是在错误顺序下兜底。两篇论文互补。

● ● ●

实现细节

RPT+ 的代码实现有一个有意思的设计选择:它用的是 Yannakakis 风格的执行框架。Yannakakis 算法是 1981 年提出的一种无环 join 处理方法——先做 semi-join reduction(用 Filter 缩小各表数据),再做最终的 join。RPT+ 的 Predicate Transfer 本质上是把 semi-join reduction 做成了运行时自适应的版本。

实验发现动态流水线(Dynamic Pipeline)是三个改进里最关键的——如果只做前两个改进不做动态决策,部分查询仍然有退化。运行时的选择性监控和 Filter 放弃机制是把"变快"和"不变慢"同时做到的核心。

● ● ●

Bottom line

给数据库加"快",难。给数据库加"只快不慢",更难。

RPT+ 的思路对任何做分析型数据库的团队都有参考价值:不做全静态的优化决策,把一部分判断推迟到运行时,用真实数据说话。Bloom Filter 不是新东西,但怎么用、何时用、用多少——这些运行时决策比静态规则管用得多。

github.com/embryo-labs/dynamic-predicate-transfer

论文:Robust Predicate Transfer with Dynamic Execution. PVLDB, 19(6): 1278-1290, 2026. doi:10.14778/3797919.3797934

文章转载自alitrack,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论