ToB企服应用市场:ToB评测及商务社交产业平台

标题: MySQL Hash Join前世今生 [打印本页]

作者: 惊雷无声    时间: 2022-11-22 02:45
标题: MySQL Hash Join前世今生
MySQL Hash Join前世今生

因工作需要,对MySQL Hash Join的内部实现做了一些探索和实践,对这个由8.0.18开始引入的连接算法有了一定的了解,写文总结与各位大佬分享,欢迎大家指教。因篇幅较长,这份总结分成若干部分,我们今天先一起来看一下MySQL Hash join的变迁史。
爬了一下 MySQL worklog[1],并结合源码及各版本的实际使用,个人认为比较重要的worklogs为如下几个, 其它的变更一般围绕这些worklogs做的小调整或bugfixes,这里我们就不一一列举。
1. WL#2241: Hash join (变更版本:8.0.18)

主要内容:
注:这里我认为官网的 Release Notes[2] 描述是不太准确的,以如下例子为例,虽然该查询包含了非等值连接条件(non-equi-join condition, 如 t1.a  t2.a ,t1.a = 1, t2.a > 1   等),但实际查询中还是使用hash join的, 因为查询语句在解析执行过程中,可能会经历语句重写、sql优化等步骤,与表象上会有所不同,建议借助explain工具,查看实际上查询语句选择的join算法。
  1. -- 版本:8.0.18
  2. -- 在创建iterator时,t1.a > 1 会被当成表的过滤条件,而非inner join的join条件
  3. -- 此查询仍使用了hash join(join 条件为空)
  4. EXPLAIN FORMAT=tree SELECT * FROM t1 JOIN t2 ON t1.i > 1;
  5. -> Inner hash join  (cost=1.17 rows=3)
  6.     -> Table scan on t2  (cost=0.34 rows=2)
  7.     -> Hash
  8.         -> Filter: (t1.i > 1)  (cost=0.65 rows=1)
  9.             -> Table scan on t1  (cost=0.65 rows=4)
复制代码
2. WL#13377: Add support for hash outer, anti and semi join( 变更版本:8.0.20)

主要内容:
  1. -- 版本:8.0.20
  2. EXPLAIN FORMAT=tree SELECT * FROM t1 JOIN t2 ON t1.i <> t2.i;
  3. -> Filter: (t1.i <> t2.i)  (cost=1.10 rows=2)
  4.     -> Inner hash join (no condition)  (cost=1.10 rows=2)
  5.         -> Table scan on t2  (cost=0.18 rows=2)
  6.         -> Hash
  7.             -> Table scan on t1  (cost=0.45 rows=2)
复制代码
  1. -- 版本:8.0.20
  2. -- Left outer join
  3. EXPLAIN FORMAT=tree SELECT * FROM t1 LEFT JOIN t2 ON t1.i=t2.i;
  4. -> Left hash join (t2.i = t1.i)  (cost=0.88 rows=4)                                                                                                                                                                          
  5.     -> Table scan on t1  (cost=0.45 rows=2)                                                                                                                                                                                    
  6.     -> Hash                                                                                                                                                                                                                    
  7.         -> Table scan on t2  (cost=0.23 rows=2)
  8. -- Right outer join(注:MySQL在parser阶段会将所有的right join改写为left join
  9. --                      所以我们这里看到的explain为Left hash join
  10. EXPLAIN FORMAT=tree SELECT * FROM t1 RIGHT JOIN t2 ON t1.i=t2.i;
  11. -> Left hash join (t1.i = t2.i)  (cost=0.88 rows=4)                                                                                                                                                                          
  12.     -> Table scan on t2  (cost=0.45 rows=2)                                                                                                                                                                                    
  13.     -> Hash                                                                                                                                                                                                                    
  14.         -> Table scan on t1  (cost=0.23 rows=2)
  15. -- Semijoin
  16. EXPLAIN FORMAT=tree SELECT * FROM t1 WHERE (t1.i) IN (SELECT t2.i FROM t2);
  17. -> Hash semijoin (t2.i = t1.i)  (cost=0.83 rows=2)
  18.     -> Table scan on t1  (cost=0.45 rows=2)
  19.     -> Hash
  20.         -> Table scan on t2  (cost=0.18 rows=2)
  21. -- Antijoin
  22. EXPLAIN FORMAT=tree
  23. SELECT * FROM t2 WHERE NOT EXISTS (SELECT * FROM t1 WHERE t1.i = t2.i);
  24. -> Hash antijoin (t1.i = t2.i)  (cost=1.10 rows=4)                                                                                                                                                                           
  25.     -> Table scan on t2  (cost=0.45 rows=2)                                                                                                                                                                                    
  26.     -> Hash                                                                                                                                                                                                                    
  27.         -> Table scan on t1  (cost=0.45 rows=2)
复制代码
  1. -- 版本: 8.0.20
  2. -- 注: t1.i > 1 会被放到hash join的 extra conditions中
  3. EXPLAIN FORMAT=tree SELECT * FROM  t1 LEFT JOIN t2 ON t1.i=t2.i AND t1.i > 1;
  4. -> Left hash join (t2.i = t1.i), extra conditions: (t1.i > 1)  (cost=0.88 rows=4)
  5.     -> Table scan on t1  (cost=0.45 rows=2)
  6.     -> Hash
  7.         -> Table scan on t2  (cost=0.23 rows=2)
复制代码
相关源码:
  1. // 代码版本:8.0.20 HEAD: commit 91a17cedb1ee880fe7915fb14cfd74c04e8d6588
  2. // 文件名:sql/hash_join_iterator.cc
  3. int HashJoinIterator::ReadNextJoinedRowFromHashTable() {
  4.   int res;
  5.   bool passes_extra_conditions = false;
  6.   do { // 所有匹配行都需要多做一个extra condition的判定,因为有可能存在不同行的记录
  7.        // 映射在同一个join key的情况,因此需要通过遍历逐条读取出符合extra conditions
  8.        // 的数据
  9.     res = ReadJoinedRow();  // 读取通过join key查找已经得到的匹配行(单行记录)
  10.     DBUG_ASSERT(res == 0 || res == -1);
  11.    
  12.     if (res == 0) {
  13.       passes_extra_conditions = JoinedRowPassesExtraConditions();
  14.       if (thd()->is_error()) {
  15.         // Evaluation of extra conditions raised an error, so abort the join.
  16.         return 1;
  17.       }
  18.       if (!passes_extra_conditions) {
  19.         ++m_hash_map_iterator;
  20.       }
  21.     }
  22.   } while (res == 0 && !passes_extra_conditions);
  23. }
复制代码
3. WL#13459: Optimize hash table in hash join (变更版本:8.0.23)

主要内容:
本篇我们对MySQL hash join的3个重要变更做了简要的总结,算是对它的前世今生有了印象,谢谢各位阅读;之后让我们会结合具体的sql查询样例,去跟踪一下对应的代码执行流程,不日更新,敬请期待。
参考文档

[1] MySQL worklog: https://dev.mysql.com/worklog/

[2] MySQL 8.0.18 release notes#optimizer: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-18.html#mysqld-8-0-18-optimizer

[3] robin hood hashing 算法介绍: https://programming.guide/robin-hood-hashing.html

[4] robin hood hasing 开源实现: https://github.com/martinus/robin-hood-hashing

[5] MySQL 8.0.23 release notes#optimizer: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-23.html#mysqld-8-0-23-optimizer

[6] MySQL worklog #13459: https://dev.mysql.com/worklog/task/?id=13459

Enjoy GreatSQL
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4