# 1. Motivation - 两表 join(binary joins) 在关系型数据库中非常普遍 - 几十年的研究使这些算法效率很高。 - 当 join 的 output 比两个 inputs 小时,只是最优的方式。 - **问题**:当 join 的 output 比两个 inputs 大时 - intermediate output 很大(需要物化) - ![](https://img.jonahgao.com/oss/note/2025p1/16721_2023_multiway_wasted.png) ------ # 2. Worst-Case Optimal Joins - Perform join by **examining a variable at a time** instead of a relation at a time. - [Size bounds and query plans for relational joins](https://arxiv.org/pdf/1711.03860.pdf)(2008) - The runtime of a WCOJ algorithm is **bounded by the output size** of the result and depends on the **variable evaluation ordering.** - If more tuples match in the intermediate results, then the DBMS must check the other tables. - The more tables a WCOJ algorithm joins, the better its performance relative to the input. > Alternative Definition #1: The worst-case runtime of the algorithm meets a known lower bound for the worst-case runtime of any join algorithm. > Alternative Definition #2: The runtime of the join algorithm is better than all other join algorithms when the query and data represent the worst possible scenario. - 到 2023 年为止,只有很少的 DBMS 支持了 WCOJ 算法 - 首批实现:LogicBlox、EmptyHeaded(Stanford) - 其他实现:RelationalAI、Umbra、DuckDB、[KuzuDB](https://kuzudb.com/blog/wcoj.html#a-thank-you--an-anecdote-about-knuths-reaction-to-the-term-worst-case-optimal) - 这些 joins 以后会变得常见,因为 SQL 2023 标准包含了图查询扩展(SQL / PGQ) ------------ # 3. Leap-Frog Trie Join - [Leapfrog Triejoin: A Simple, Worst-Case Optimal Join Algorithm](https://openproceedings.org/ICDT/2014/paper_13.pdf) - 执行 join 前,relations 必须按照 join keys 排序 或者实时构建 trie index - 将具有多个 attributes 的 relation 表示为 tries - 一个 relation 一个 trie - trie 的每一个 level 代表了 join keys 中的一个 attriubte - Developed by LogicBlox in the early 2010s - **排序的方式:** - ![](https://img.jonahgao.com/oss/note/2025p1/16721_2023_multiway_trie_sort.png) - **Trie 的形式:** - ![](https://img.jonahgao.com/oss/note/2025p1/16721_2023_multiway_trie_form1.png) - ![](https://img.jonahgao.com/oss/note/2025p1/16721_2023_multiway_trie_form2.png) - 如果文件是 read-only 时,可以预先生成 trie ## Observation - 实时为每个 relation 构建一棵 Trie 树是昂贵的 - 即使 database 是 read-only 的,为每个可能的 join ordering 预先构建 tries 也不现实 - 另一种方案是使用 nested hash tables,但是也比较昂贵 - 检查 hash 冲突至少需要一次 key 比较 - 需要存储实际的 keys 或者指向 tuples 的指针,来处理冲突。(带来许多 cache misses) - 需要使用 dictionary encoding 来处理变长 keys --------- # 4. Hash Trie Join ## Multi-Way Hash Trie Joins - [Adopting worst-case optimal joins in relational database systems](https://dl.acm.org/doi/10.14778/3407790.3407797) - 不在 nested hash tables 中存储 join keys,而是存储 hash values。 - 没有 type-sepcific 逻辑来计算 set intersections 和 lookup 操作 - DBMS 只需要使用整数比较(很快) - 当 hash 冲突时需要 remove false positive matches ## 4.1 Hash Trie - ![](https://img.jonahgao.com/oss/note/2025p1/16721_2023_multiway_hash_trie1.png) - 来自:[Michael Freitag](https://db.in.tum.de/~freitag/) ### 4.1.1 Tagged Pointers - 利用指向 trie node 的64位指针的未使用部分 来存储额外的元数据 - x86-64 内存地址只使用 48 bits - 元数据:所指向 trie node 的信息 - ![400](https://img.jonahgao.com/oss/note/2025p1/16721_2023_multiway_hash_trie_tagged.png) - **1-bit**:Singleton Flag - **1-bit**:Expansion Flag 是否指向了一个已经 expanded 的 node - **14-bits**:Chain Length - **48-bits**:Memory Address ### 4.1.2 Singleton Pruning - trie lower levels 的 hash tables 会变小 - inner nodes 经常只指向一个 tuple - 优化:不再存储到 leaf node 的每层 trie level,而是存储直接指向 singleton tuple 的指针 - ![400](https://img.jonahgao.com/oss/note/2025p1/16721_2023_multiway_hash_trie_prune.png) ### 4.1.3 Lazy Child Expansion - 根据 intersection 操作的 selectivity, DBMS 不会访问太多 inner nodes - 优化:只有在 probe 阶段被访问时,才物化 inner nodes - root node 是特殊的,始终物化 - ![400](https://img.jonahgao.com/oss/note/2025p1/16721_2023_multiway_hash_trie_lazy.png) - 访问时,发现 link list,再创建中间节点 ------------- # Observation - 如果中间结果不大于它的 inputs,则 Multi-way joins 慢于 binary joins - Umbra 扩展了它们的优化器,使用启发式方法来决策使用 binary join 还是 WCOJ。 --------- # Parting Thoughts - Multi-way joins 是一个活跃的研究领域 - 接下来的十年中,**它们可能会成为关系数据库中 join 的默认选择** - **这会是图数据库的致命一击**