MySQL8.0.18新特性之bash join探究

【引言】
今年9月14日的OOW19大会,大会内容《python and mysql 8.0 document store》topic中提到MySQL即将在8.0.18中支持hash join;Hash Join引入MySQL将给SQL的性能带来显著提升。

查了下,Oracle数据库在1996年7.3版本中就已经推出了hash join功能。

2019年10月14日,oracle正式发布MySQL8.0.18,有哪些改进changes,请详见官方大纲,详细链接如下:
https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-18.html
Changes in MySQL 8.0.18 (2019-10-14, General Availability)
image.png

前有9月14日的OOW19的MySQL即将在8.0.18中支持hash join的铺垫,后有10月14日8.0.18的版本发布,这个数据库圈整的挺让人兴奋;故本文跟风介绍Hash Join的那点事。

本文大纲:
1.啥是hash join?
2.为啥MySQL现在才实现hash join?
3.结语

一、 hash join是个嘛?
先看下MySQL8版本的hash join官方介绍
image.png
翻译如下:
Hash Join (WL#2241) 此功能由 Erik Froseth 实现,为 MySQL 中执行内部等价联接的一种方式。
例如:SELECT*FROM t1 JOIN t2 ON t1.col1=t2.col1;
可以在 8.0.18 中作为 Hash Join 执行。Hash Join 不需要任何索引来执行,并且在大多数情况下比当前的块嵌套循环算法更有效。
示例如下:
image.png
敲重点:
根据MySQL官方文档介绍,Hash Join不需要任何索引来执行,并且在大多数情况下比当前的块嵌套循环算法更有效。

是不是没太懂,接下来抛开具体版本,介绍下啥是Hash Join。

hash join是一种数据库在进行多表连接时的处理算法,对于多表连接还有两种比较常用的方式:sort merge-join 和 nested loop。

大家都知道,oracle,postgresql都已支持hash-join,mysql8.0.17之前版本是不支持的。hash-join本身的实现并不是很复杂,但需要优化器的实现配合才能最难的地方。

多表连接分为以下几种:内连接(inner join),外连接(inner join)和交叉连接。外连接又分为:左外连接,右外连接和全外连接。对于不同的查询方式,使用相同的join算法也会有不同的代价产生,具体使用哪一种连接方式是由优化器通过代价的衡量来决定。

这里介绍下三种join方式逐一介绍

扫描二维码关注公众号,回复: 9985935 查看本文章

1. nested loop join
嵌套循环连接,很常见的连接方式,分为内外表,外表每扫描一行数据均需在内表中查找与之相匹配的行。不加索引的复杂度是O(N*M),复杂度对大数据集太不友好,通常用+索引来提升性能。
示例:select * from a,b where a.id=b.id;

在嵌套循环中,内表被外表驱动,外表返回的每一行均需在内表中检索找到与之匹配的行,整个查询返回的结果集不能太大(通常认为应<10,000行)。要把返回子集较小表的作为外表,CBO 默认外表为驱动表,且应在内表的连接字段上要有索引。

注意:Nested loop一般用在连接的表中有索引;当然也可用ORDERED 提示来改变CBO默认的驱动表,USE_NL(table_name1 table_name2)可强制CBO 执行嵌套循环连接。

2.  sort merge-join
merge join需要首先对两个表按照关联的字段进行排序;排序合并连接原理是先对两个表/行源根据JOIN列进行全扫描后排序,然后再进行连接。排序合并连接可以处理非等值JOIN。排序合并连接非常耗费资源,主因是对要连接的表/结果集进行排序,通常情况下,CBO是不会优先选择该表连接方式。
示例:select * from a,b where a.id<=b.id;

注意:
可以使用USE_MERGE(table_name1 table_name2)来强制使用排序合并连接。
        
Sort Merge join 用在没有索引,并且数据已经排序的情况。
通常走Sort Merge join的场景:
1.RBO模式
2.不等价关联(>,<,>=,<=,<>)
3.HASH_JOIN_ENABLED=false
4.数据源已排序

3. Hash join
hash-join是CBO 做大数据集连接时的常用方式,对于两个表来讲,优化器使用两个表中较小的表利用连接键在内存中建立hash表,然后扫描较大的表,找出与hash表中匹配的行。因hash表是放在内存中,所以可很快的得到对应的hash表与较大表相匹配的行。

这种方式适用于较小的表完全可以放于内存中的情况,总成本就是访问两个表的成本之和。但如果HASH表太大,无法一次构造在内存中,这时优化器会将它分割成若干不同的partition,不能放入内存的部分就把该分区写入磁盘的temporary segment,此时要有较大的临时段从而尽量提高I/O 的性能;因会多一个写的代价,会降低效率。

可以用USE_HASH(table_name1 table_name2)提示来强制使用散列连接。

好了,接下来要讲一讲优化器了。

大家都知道,优化器的处理是比较复杂,也是sql执行过程张最难的地方,优化器没有最优只有更优。目前的优化器大都属于CBO(COST-BASED OPTIMIZER)类型。优化器的路径选择会依据一些统计信息,如列最大值、最小值、直方图、DISTINCT值等,找出消耗CPU和内存最少的sql执行路径。

优化器作用在于路径选择,多表连接如何确定表连接的顺序和连接方式;不同的数据库有着不同的选择,pg支持动态规划算法,表数量过多的时候使用遗传算法。

因为本文讲的是hash join,所以这里先看下hash join的实现步骤:
1.hash join的实现分为build table也就是被用来建立hash map的小表和probe table,hash join本身的实现不要去判断哪个是小表,优化器生成执行计划时就已经确定了表的连接顺序;
2.首先依次读取小表的数据,对于每一行数据根据连接条件生成一个hash map中的一个元组;
3.数据缓存在内存中,如内存放不下需dump到外存temporary segment上;
4.依次扫描探测表拿到每一行数据根据join condition生成hash key映射hash map中对应的元组,元组对应的行和探测表的这一行有着同样的hash key, 这时并不能确定这两行就是满足条件的数据,需要再次过一遍join condition和filter,满足条件的数据集返回需要的投影列。

Hash join的代价值:
cost = (outer access cost * # of hash partitions) + inner access cost+JOIN_CONDITION_COST + FILTER_COST

注意:
hash table的大小、需要分配多少个patition是一个问题,分配太大会造成内存浪费,分配太小会导致一旦超过内存限制,会dump到外存,不同数据库有不同的实现方式。

小结:
1.Hash join通常将相对小的那个表做hash运算,将关联列数据存储到hash列表中,从另一个表中抽取记录,做hash运算,到hash 列表中找到相应的值,做关联比对。
2.Nested loops 是从一张表中读取数,访问另一张表来做匹配(关联属性通常要建index),适用场合是关联表较小时,效率会更高。
3.Merge Join 是先将关联表的关联列各自先行做排序,从各自的排序表中抽取数据,到另一个排序表中做匹配。merge join的主要耗时体现在关联了的排序上,故消耗的资源更多。

二、 为啥MySQL现在才实现hash join?
MySQL8.0.18版本前,一直不支持hash join,以下有几点个人推断:

  1. 先从技术开发谈
    Hash Join的算法本身不复杂,但要让Hash join运行的很好,就涉及一个话题:需完善的CBO优化器,类似Oracle CBO。大家知道,MySQL8.0.18版本前,其优化器部分为CBO优化器,有一部分是RBO优化器,不像Oracle那样完全实现CBO功能;CBO依赖于统计信息,涉及谓词越界、数据分布不均、绑定变量窥探、直方图、聚簇因子等问题均需考虑。实现Hash Join背后的逻辑和数据支撑均需要发给较高的技术开发和维护成本,才能让Hash join充分落地;对MySQL源代码改动太大,开发成本比较高,需要一个开发过程;

2.竞争对手PG的压力
从上图DB-ENGINE排名看,紧随Mysql的开源库竞争对手PG早已经实现了hash join;MySQL当然不甘落后。在今年9月份的OOW大会,随着Oracle商业库智能自治特性,MySQL推出Hash join功能实现,营造一种遍地开花的市场效果。

3.Oracle高层决议
MySQL被Oracle收购后,对于MySQL的开发定位和走向,也是需要Oracle方面综合考虑,毕竟要兼顾Oracle商业库产品;一方面MySQL肯定是要进行发展,借助MySQL和其他开源产品,尤其是PG,mariaDB进行PK,将Oracle商业库的一些好的特性可以移植到MySQL上产生技术优势;另一方面,也在考虑Oracle商业库的发展,不能让MySQL喧宾夺主。
4.市场成熟度/竞争
另,随着万物皆开源的大势,Oracle收购Mysql确是一步好棋,借助oracle牛逼的销售体系和丰富的文档库,Mysql发展势头迅猛,已经和老大哥Oracle数据库不相上下;详见下图11月份最新的DB-ENGINE排名(链接:https://db-engines.com/en/ranking)。
image.png
认识到其重要性,oracle公司自然会加大对Mysql的开发支持,刚说了,因为oracle库已经
在1996年7.3版本中就推出了hash join功能,Mysql借鉴Oacle的功能也是自然之事;这也是市场越来越多的人说mysql库越来越像oracle的原因。所以,前面第一点说的原因也就不是原因,而是不得不得事情了。

请注意:以上几点,仅为个人见解。

【总结】
1.多表之间的连接有三种方式:Nested Loops,Hash Join 和 Sort Merge Join;
2.通常情况下散列连接的效果都比排序合并连接要好,然而如果行源已经被排过序,在执行排序合并连接时不需要再排序了,这时排序合并连接的性能会优于散列连接;
3.对于结果集很大的情况,merge-join因需对表排序,所以效率并不会很高;nested loop join是一种嵌套循环的查询方式,更不适合大数据集join;hash-join正是为处理这种棘手的查询方式而生,尤其是对一个大表和一个小表的情况,只需要将大小表扫描一遍就可以得出最终的结果集;
4.hash-join只适用于等值连接,对于>, <, <=, >=这样的查询连接还是需要nested loop这种通用的连接算法来处理。如果连接key本来就是有序的或者需要排序,那么可能用merge-join的代价会比hash-join更小,此时merge-join会更有优势。

【参考】
https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-18.html
【参考】
https://weibo.com/p/1001603899778529412913
【参考】
http://www.sohu.com/a/341847821_505827
【参考】
https://www.cnblogs.com/shangyu/p/6055181.html
【参考】
https://blog.csdn.net/leo0805/article/details/78180067

以下为个人公众号“一森咖记”,欢迎关注。
image.png

发布了54 篇原创文章 · 获赞 3 · 访问量 5508

猜你喜欢

转载自blog.csdn.net/db_murphy/article/details/103160631