网易首页 > 网易号 > 正文 申请入驻

MySQL对JOIN做了那些不为人知的优化《死磕MySQL系列 十七》

0
分享至

大家好,我是咔咔不期速成,日拱一卒

通过上期文章知道了在MySQL中存在三种join的算法,分别为NLJ、BNLJ、BNL,总结来说分为索引嵌套循环连接、缓存块嵌套循环连接、粗暴循环连接。

另外还知道了一个新的概念join_buffer,作用就是把关联表的数据全部读入join_buffer中,然后从join_buffer中一行一行的拿数据去被驱动表中查询。由于是在内存中获取数据,因此效率还是会有所提升。

同时在上期文章中遇到了一个陌生的概念hash_join,在上期中没有详细说明,本期会进行详述。

在介绍本期主题时先来了解一个知识点Multi-Range Read,主要的作用是尽量让顺序读盘,在任何领域只要是有顺序的都会有一定的性能提升。

比如MySQL的索引,现在你应该知道索引天生具有有序性从而避免服务器对数据再次排序和建立临时表的问题。

接下来使用一个案例来实操一下这个优化是怎么做的

创建join_test1、join_test2两张表

CREATE TABLE `join_test1` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`a` int(11) unsigned NOT NULL,
`b` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

CREATE TABLE `join_test2` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`a` int(11) unsigned NOT NULL,
`b` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

给两张表添加一些数据,用于案例演示

drop procedure idata;
delimiter ;;
create procedure idata()
begin
declare i int;
set i=1;
while(i<=1000)do
insert into join_test1 (a,b) values ( 1001-i, i);
set i=i+1;
end while;

set i=1;
while(i<=1000000)do
insert into join_test2 (a,b) values (i, i);
set i=i+1;
end while;

end;;
delimiter ;
call idata();

表join_test1的字段a上存在索引的,那么在查询时就会使用该索引。

执行流程大致为获取到字段a所有的值,然后根据a的值一行一行的进行回表到主键索引上获取数据

现在的情况是如果随着a的值递增顺序查询的话,id的值就会变相的为倒叙,虽然看起来是根据主键ID连续倒叙的,但在生产环境下肯定不是连续的,就会造成随机访问,那就肯定会造成性能变差。

为什么说随机访问会影响性能?

MySQL的索引天生具有有序性,同时MySQL也同样借鉴了局部性原理,局部性原理是数据和程序都默认有聚集成群的倾向,在访问到一行数据后,会有极大可能性再次访问到这条数据或这条数据相邻的数据。

现在你应该知道了MySQL在读取数据时并不是只读查询的数据,默认会读取16kb的数据,这个值是根据innodb_page_size决定的。

因此顺序查询是非常快的,是因为不用每次都通过执行器获取数据,而是直接在内存中获取,但若访问变为随机性就会每次通过执行器进行获取数据,所以这才是性能变差的原因。

MRR的作用

说了这么多现在你应该知道了MRR的作用就是把查询变为主键ID的递增查询,对磁盘的读尽可能的接近顺序读,就可以提升性能。

因此,执行语句的执行流程就会变成这样

  • 先根据索a,获取到所有满足条件的数据,并且将主键id的值放入read_rnd_buffer中

  • 在read_rnd_buffer中把id的值进行正序排序

  • 再根据排序后得主键ID值,依次到主键索引上获取数据,并返回结果集

如何开启read_rnd_buffer

read_rnd_buffer的大小是由read_rnd_buffer_size参数控制的,默认值为256kb,但你要知道的是对于MRR的优化在优化器的判断策略中会更倾向于不使用,如果要使用则需要进行配置修改即可。

set optimizer_switch="mrr_cost_based=off"

read_rnd_buffer存不下怎么办?

回忆下在上期中提到的join_buffer不够用是怎么处理的,会把上次读取的数据从buffer中清空,再放入剩下的数据,在MySQL中对于存储结果集的buffer内存不够情况下大多数都是这么处理的。

使用了read_rnd_buffer后的SQL执行流程就变成了这样

explain的结果显示

注意点

假设现在把查询范围扩大,看一下会有什么变化

可以看到当把范围扩大至接近全表数据时,会不再使用索引a从而进行了全表扫描,也就无法再使用mrr优化了

因此想要使用MRR进行提升性能是基于两个非常重要的点,一个是在索引上进行范围查询,另一个就是必须能使用上索引,当然这个索引要是范围查询的列

二、Nested-Loop Join优化

快一个月没更文了,对Nested-Loop Join的算法还能回忆多少,SQL的执行流程大致如下:

从join_test1表读取一行数据R

  • 从R中取id字段到表join_test2去查找索引a,并通过主键ID获取到满足的行

  • 取出join_test2中满足条件的行,跟R组成一行

  • 重复前三个步骤,直到表join_test1满足条件的数据扫描结束

  • NLJ算法的逻辑就是从驱动表取一行数据后就直接到被驱动表中做join操作,对于驱动表来说就变成了每次都匹配一个值,这时就不满足MRR优化的条件了。

    通过上期文章,现在你应该知道了join_buffer在BNL算法中的作用,但在NLJ算法中并没有使用。

    那想办法把驱动表的数据批量传给被驱动表进行join操作不就行了?

    没错,MySQL团队在5.6版本引入了此方案,在驱动表中取出一部分数据,放到临时内存,这个临时内存就是上期的join_buffer。

    那么执行流程图就会变成这样

    这里需要注意没有把索引a在read_rnd_buffer中的流程画出来,如果不理解就到上文去看那副图哈!

    上图中,我们依然查询了1000条数据,那么join_buffer就会存着1000条数据,如果存不下就会分段进行,直到执行结束。

    对于NLJ算法的优化官方也给起来了一个名为Batched Key Access

    BKA算法的启用

    既然要使用MRR优化,那就要开启MRR,开启MRR的同时还要开启batched_key_access=on即可

    set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
    三、Block Nested-Loop Join算法优化

    非常简单的优化就是在被驱动表上添加索引,这时BNL的算法就自然而然的变为BKA算法了

    select * from t1 join t2 on (t1.b=t2.b) where t2.b>=1 and t2.b<=2000;

    这条SQL在join_test2上只查询了2000行数据,如果你的MySQL机器对内存不那么看重的话直接给字段b加个索引即可。

    反之,就需要另辟奇径了

    再来复习下BNL算法的执行流程

    • 取出join_test1的所有数据,存储join_buffer中

    • 扫描join_test2用每行数据跟join_buffer中的数据进行对比,不满足跳过,满足存储结果集

    由于被驱动表字段b是没有索引的,因此从join_buffer中读取出来的每条数据都要对join_test2进行全表扫描。

    案例中join_test2表共100W数据,那么需要扫描的行数就是1000*100W = 10亿次,只需要2000条数据却要执行10亿次,这个性能可想而知。

    这时,我们就可以使用奇径临时表来解决这个问题,实现思路大致如下

    • 先把join_test2中满足条件的数据存放在临时表中tmp_join_test2中

    • 此时临时表的数据只有条件范围的2000数据,因此是完全可以给字段b添加索引的

    • 最后再让join_buffer跟tmp_join_test2做join操作

    对应的SQL操作如下

    create temporary table tmp_join_test2 (id int primary key, a int, b int, index(b))engine=innodb;
    insert into tmp_join_test2 select * from join_test2 where b>=1 and b<=2000;
    explain select * from join_test1 join tmp_join_test2 on (join_test1.b=tmp_join_test2.b);

    扫描行数

    insert 是对表join_test2进行的全表扫描,此时扫描行数为100W行

    join_test1进行全表扫描一次扫描行数为1000行

    每次join操作是一条数据,共计1000次,扫描行数为1000行

    使用了临时表后总体扫描行数从10亿次到了100W+2000次,执行查询的结果返回预计都不到一秒时间。

    总结

    不管是使用BKA算法还是使用临时表都有一个共同点,那就是让被驱动表上能用上索引来主动触发BKA算法,从而提升性能。

    四、Hash join

    大家还记得这幅图吧!上期文章中复现Block Nested-Loop Join算法呢!结果返回了一个hash_join,上期并没有说明。

    因为hash_join算法是在MySQL8.0.18才有的

    hash_join生效的前提是被驱动表join的字段没有索引,在MySQL8.0.18中还有一个约束就是条件对等,例如案例中的join_test1.b=tmp_join_test2.b

    但在8.0.20中取消了条件对等的约束,并全面支持non-equi-join,Semijoin,Antijoin,Left outer join/Right outer join

    其实hash_join算法的实现原理很简单

    • 驱动表中的join字段进行计算hash值

    • 在内存中创建一个hash_table,把驱动表所有的hash值存放进去

    • 获取被驱动表中满足条件的数据,例如join_test2中的select * from join_test2 where b>=1 and b<=20002000行数据

    • 把这2000行数据,一行一行的跟hash_table中的数据进行对比,条件满足的数据作为结果集进行返回

    可以看到hash_join算法的扫描行数跟临时表大差不差,那么为什么MySQL会默认使用hash_join这种算法呢?这个问题就要留给大家去深究了

    五、总结

    本期主要分享了NLJ、BNJ的算法优化

    在这些优化中,hash_join在MySQL8.0.18中已经内置支持了,但低版本的还是默认为BKA算法

    建议给被驱动表需要join字段加上索引,把BNL算法转为BKA或者hash_join算法

    同时还给大家提供了一个临时表的方案,临时表在开发过程中是非常容易忽略的一个优化点,可以在适当的环境下学会使用临时表

    “ 坚持学习、坚持写作、坚持分享是咔咔从业以来所秉持的信念。愿文章在偌大的互联网上能给你带来一点帮助,我是咔咔,下期见。 ”

    特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

    Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

    相关推荐
    热点推荐
    乔治:快船交易交易哈登后,我和莱昂纳德只能干脏活累活!

    乔治:快船交易交易哈登后,我和莱昂纳德只能干脏活累活!

    历史第一人梅西
    2024-06-20 21:32:10
    上海一女子在整理丈夫遗物时,却无意中发现丈夫生前曾给其前女同事转赠100万元

    上海一女子在整理丈夫遗物时,却无意中发现丈夫生前曾给其前女同事转赠100万元

    华庭讲美食
    2024-06-17 07:10:22
    长公主被家暴到不能生了

    长公主被家暴到不能生了

    毒舌扒姨太
    2024-05-27 22:24:51
    突发改口!495天以来,房地产第一次

    突发改口!495天以来,房地产第一次

    说财猫
    2024-06-20 22:35:36
    两瓶水的较量画上了句号,胜负已定,只是我们不甘心娃哈哈的结局

    两瓶水的较量画上了句号,胜负已定,只是我们不甘心娃哈哈的结局

    华商天下
    2024-06-07 16:08:07
    布伦森和娇妻近照,季后赛得分王,1亿合同白菜价,妻子很漂亮

    布伦森和娇妻近照,季后赛得分王,1亿合同白菜价,妻子很漂亮

    大西体育
    2024-05-02 21:05:11
    桂林象鼻山“鼻子”差点被洪水淹没 景区:已闭园,退水后会检查是否受损

    桂林象鼻山“鼻子”差点被洪水淹没 景区:已闭园,退水后会检查是否受损

    极目新闻
    2024-06-20 20:14:05
    多人喝瑞幸西梅美式后腹泻,客服:赠送纸巾“没有内涵的意思”

    多人喝瑞幸西梅美式后腹泻,客服:赠送纸巾“没有内涵的意思”

    天天财经116
    2024-06-19 18:33:23
    骂不过根本骂不过, 感觉全网都怼不过林更新啊  博士不是白考的

    骂不过根本骂不过, 感觉全网都怼不过林更新啊 博士不是白考的

    钱多多多多
    2024-06-19 15:24:56
    黄一鸣有法律资格证?支持女儿合法继承财产,王思聪只剩一招救命

    黄一鸣有法律资格证?支持女儿合法继承财产,王思聪只剩一招救命

    每日漓说
    2024-06-20 00:16:08
    买超豪掷百万买钻戒求婚21岁邵晴!张嘉倪净身出户,邵晴晋升阔太

    买超豪掷百万买钻戒求婚21岁邵晴!张嘉倪净身出户,邵晴晋升阔太

    八卦王者
    2024-06-19 11:20:15
    闹大了!上海一女子称三代都是税务局人,评论炸锅,官方回应来了

    闹大了!上海一女子称三代都是税务局人,评论炸锅,官方回应来了

    鹏飞深文
    2024-05-01 14:42:24
    恭喜九爷,50岁因扎吉与34岁未婚妻大婚,两人育有两个孩子

    恭喜九爷,50岁因扎吉与34岁未婚妻大婚,两人育有两个孩子

    懂球帝
    2024-06-20 18:05:18
    城管队长出轨女下属称对方“嫩妈”,女主照片曝光真白瞎这张脸!

    城管队长出轨女下属称对方“嫩妈”,女主照片曝光真白瞎这张脸!

    听风听你
    2024-05-30 20:29:17
    麻六记曝光马筱梅旗袍照片,网友:简直像极了阿娇!

    麻六记曝光马筱梅旗袍照片,网友:简直像极了阿娇!

    轻嗅蔷薇
    2024-06-20 10:48:05
    烟草局凌晨上门伪造证据,辩称“程序合法”,公权力失控有多可怕

    烟草局凌晨上门伪造证据,辩称“程序合法”,公权力失控有多可怕

    毒哥的毒鸡汤
    2024-06-19 12:20:48
    以色列宣布一个月内解决所有哈马斯!下一个是真主党?

    以色列宣布一个月内解决所有哈马斯!下一个是真主党?

    项鹏飞
    2024-06-19 15:33:32
    外交部:中方将与美、澳启动新一轮大熊猫保护国际合作

    外交部:中方将与美、澳启动新一轮大熊猫保护国际合作

    人民资讯
    2024-06-20 19:06:08
    撞脸陈紫函,蒋欣瘦到认不出,几段感情无疾而终,至今仍单身一人

    撞脸陈紫函,蒋欣瘦到认不出,几段感情无疾而终,至今仍单身一人

    山野下
    2024-06-20 10:45:10
    《玫瑰的故事》火爆全网后,“奇怪”的现象发生了,林更新没说错

    《玫瑰的故事》火爆全网后,“奇怪”的现象发生了,林更新没说错

    喵喵娱乐团
    2024-06-20 20:23:24
    2024-06-21 00:42:45
    原来是咔咔丫
    原来是咔咔丫
    你为技术,技术为你
    22文章数 48关注度
    往期回顾 全部

    科技要闻

    小米SU7流量泼天,富贵却被蔚来接住了

    头条要闻

    媒体:以为中国会服软 菲在南海主权之争上存低级误判

    头条要闻

    媒体:以为中国会服软 菲在南海主权之争上存低级误判

    体育要闻

    千夫所指的关系户 成了拯救葡萄牙的英雄

    娱乐要闻

    叶舒华参加柯震东生日聚会,五毒俱全

    财经要闻

    楼市新“王炸”!释放何信号?

    汽车要闻

    售价11.79-14.39万元 新一代哈弗H6正式上市

    态度原创

    亲子
    旅游
    健康
    教育
    军事航空

    亲子要闻

    宝宝不过是想用洗衣机洗个袜子,却不想引起了新一轮商战!

    旅游要闻

    铁路儿童票新规 已有超4900万小旅客免费出行

    晚餐不吃or吃七分饱,哪种更减肥?

    教育要闻

    学霸的题目,当然要学霸来做了!学渣挤眉弄眼一个字都没写出来

    军事要闻

    以军发言人公开表示"哈马斯无法被消灭" 以政府反驳

    无障碍浏览 进入关怀版