MySQL 实战 45 讲
林晓斌
网名丁奇,前腾讯云数据库负责人
224874 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 49 讲
实践篇 (37讲)
特别放送 (1讲)
结课测试 (1讲)
MySQL 实战 45 讲
15
15
1.0x
00:00/00:00
登录|注册

35 | join语句怎么优化?

MRR的explain结果
MRR的执行流程
使用临时表
在被驱动表上建索引
BNL算法的影响
Buffer Pool的影响
BKA算法的启用方法
BKA算法的流程
Multi-Range Read优化
MRR的设计思路
自己实现hash join
基于临时表的改进方案
BNL算法优化
BKA优化
扩展 - hash join
BNL转BKA
BNL算法的性能问题
Batched Key Access (BKA)算法
MRR优化
下期问题
总结
Index Nested-Loop Join (NLJ)
join语句的优化怎么做?

该思维导图由 AI 生成,仅供参考

在上一篇文章中,我和你介绍了 join 语句的两种算法,分别是 Index Nested-Loop Join(NLJ) 和 Block Nested-Loop Join(BNL)。
我们发现在使用 NLJ 算法的时候,其实效果还是不错的,比通过应用层拆分成多个语句然后再拼接查询结果更方便,而且性能也不会差。
但是,BNL 算法在大表 join 的时候性能就差多了,比较次数等于两个表参与 join 的行数的乘积,很消耗 CPU 资源。
当然了,这两个算法都还有继续优化的空间,我们今天就来聊聊这个话题。
为了便于分析,我还是创建两个表 t1、t2 来和你展开今天的问题。
create table t1(id int primary key, a int, b int, index(a));
create table t2 like t1;
drop procedure idata;
delimiter ;;
create procedure idata()
begin
declare i int;
set i=1;
while(i<=1000)do
insert into t1 values(i, 1001-i, i);
set i=i+1;
end while;
set i=1;
while(i<=1000000)do
insert into t2 values(i, i, i);
set i=i+1;
end while;
end;;
delimiter ;
call idata();
为了便于后面量化说明,我在表 t1 里,插入了 1000 行数据,每一行的 a=1001-id 的值。也就是说,表 t1 中字段 a 是逆序的。同时,我在表 t2 中插入了 100 万行数据。

Multi-Range Read 优化

在介绍 join 语句的优化方案之前,我需要先和你介绍一个知识点,即:Multi-Range Read 优化 (MRR)。这个优化的主要目的是尽量使用顺序读盘。
第 4 篇文章中,我和你介绍 InnoDB 的索引结构时,提到了“回表”的概念。我们先来回顾一下这个概念。回表是指,InnoDB 在普通索引 a 上查到主键 id 的值后,再根据一个个主键 id 的值到主键索引上去查整行数据的过程。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了如何优化join语句,主要包括Multi-Range Read(MRR)优化和Batched Key Access(BKA)算法。MRR优化旨在通过范围查询和排序,提升性能,而BKA算法则是对Index Nested-Loop Join(NLJ)算法的优化,通过将驱动表的数据暂存到join_buffer,一次性传递多个值给被驱动表,以提高效率。文章详细解释了这两种优化方法的原理和执行流程,并指出了使用这些优化方法的注意事项。此外,还介绍了BNL算法对系统的影响以及BNL转BKA的优化方法。最后,还提到了哈希join的优化思路。总的来说,本文为读者提供了关于join语句优化的核心思想和具体实现方式,对优化数据库查询性能提供了有益的参考。文章还提出了一个思考题,让读者思考如何设计表t1、t2、t3上的索引来支持一个三个表join的需求,并用straight_join来重写这个语句,配合创建的索引。这为读者提供了一个实际问题来应用所学的知识。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《MySQL 实战 45 讲》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(118)

  • 最新
  • 精选
  • 老杨同志
    我准备给 t1增加索引c t2增加组合索引b,c t3增加组合索引b,c select * from t1 straight_join t2 on(t1.a=t2.a) straight_join t3 on (t2.b=t3.b) where t1.c>=X and t2.c>=Y and t3.c>=Z; 另外我还有个问题,开篇提到的这句sql select * from t1 where a>=1 and a<=100; a是索引列,如果这句索引有order by a,不使用MRR 优化,查询出来就是按a排序的,使用了mrr优化,是不是要额外排序

    作者回复: 对,好问题,用了order by就不用MRR了

    2019-02-01
    5
    61
  • Mr.Strive.Z.H.L
    老师您好,新年快乐~~ 关于三表join有一个疑惑点需要确认: 老师您在评论中说到,三表join不会是前两个表join后得到结果集,再和第三张表join。 针对这句话,我的理解是: 假设我们不考虑BKA,就按照一行行数据来判断的话,流程应该如下(我会将server端和innodb端分的很清楚): 表是t1 ,t2 ,t3。 t1 straight_join t2 straight_join t3,这样的join顺序。 1. 调用innodb接口,从t1中取一行数据,数据返回到server端。 2. 调用innodb接口,从t2中取满足条件的数据,数据返回到server端。 3. 调用innodb接口,从t3中取满足条件的数据,数据返回到server端。 上面三步之后,驱动表 t1的一条数据就处理完了,接下来重复上述过程。 (如果采用BKA进行优化,可以理解为不是一行行数据的提取,而是一个范围内数据的提取)。 按照我上面的描述,确实没有前两表先join得结果集,然后再join第三张表的过程。 不知道我上面的描述的流程对不对?(我个人觉得,将innodb的处理和server端的处理分隔清晰,对于sql语句的理解,会透彻很多)

    作者回复: 新年快乐,分析得很好。 可以再补充一句,会更好理解你说的这个过程 : 如果采用BKA进行优化,每多一个join,就多一个join_buffer

    2019-02-02
    51
  • 天王
    join语句的优化,NLJ算法的优化,MRR优化器会在join_buffer进行主键的排序,然后去主键索引树上一个个的查找,因为按照主键顺序去主键索引树上查找,性能会比较高,MRR优化接近顺序读,性能会比较高。BKA算法是对NLJ算法的优化,一次取出一批数据的字段到join_buffer中,然后批量join,性能会比较好。BKA算法依赖于MRR,因为批量join找到被驱动表的非聚集索引字段通过MRR去查找行数据

    作者回复: 👍

    2019-02-13
    3
    33
  • 郭健
    老师,有几个问题还需要请教一下: 1.上一章t1表100条数据,t21000条数据,mysql会每次都会准确的找出哪张表是合理的驱动表吗?还是需要人为的添加straight_join。 2.像left join这种,左边一定是驱动表吧?以左边为标准查看右边有符合的条件,拼成一条数据,看到你给其他同学的评论说可能不是,这有些疑惑。 3.在做join的时候,有些条件是可以放在on中也可以放在where中,比如(t1.yn=1 和t2.yn=1)这种简单判断是否删除的。最主要的是,需要根据两个条件才能join的(productCode和custCode),需要两个都在on里,还是一个在on中,一个在where中更好呢?

    作者回复: 1. 正常是会自己找到合理的,但是用前explain是好习惯哈 2. 这个问题的展开我放到答疑文章中哈 3. 这也是好问题,需要分析是使用哪种算法,也放到答疑文章展开哈。 新年快乐~

    2019-02-07
    2
    31
  • asdf100
    最近遇到这个需求,in里面的值个数有5万左右,出现的情况很少但存在,这种情况怎么处理。?手动创建临时表再join? 另外in内的值用不用手动排序?

    作者回复: 不需要手动排序 不过5万个值太凶残了,语句太长不太好 这种就是手动创建内存临时表,建上hash索引,填入数据,然后join

    2019-02-01
    8
    25
  • HuaMax
    前提假设:t1.c>=X可以让t1成为小表。同时打开BKA和MRR。 1、t1表加(c,a)索引。理由:A、t1.c>=X可以使用索引;B、加上a的联合索引,join buffer里放入的是索引(c,a)而不是去主键表取整行,用于与表t2的t1.a = t2.a的join查询,不过返回SELECT * 最终还是需要回表。 2、t2表加(a,b,c)索引。理由:A、加上a避免与t1表join查询的BNL;B、理由同【1-B】;C、加上c不用回表判断t2.c>=Y的筛选条件 3、t3表加(b,c)索引。理由:A、避免与t2表join查询的BNL;C、理由同【2-C】 问题: 1、【1-B】和【2-B】由于select *要返回所有列数据,不敢肯定join buffer里是回表的整行数据还是索引(c,a)的数据,需要老师解答一下;不过值得警惕的是,返回的数据列对sql的执行策略有非常大的影响。 2、在有join查询时,被驱动表是先做join连接查询,还是先筛选数据再从筛选后的临时表做join连接?这将影响上述的理由【2-C】和【3-C】 使用straight_join强制指定驱动表,我会改写成这样:select * from t2 STRAIGHT_JOIN t1 on(t1.a=t2.a) STRAIGHT_JOIN t3 on (t2.b=t3.b) where t1.c>=X and t2.c>=Y and t3.c>=Z; 考虑因素包括: 1、驱动表使用过滤条件筛选后的数据量,使其成为小表,上面的改写也是基于t2是小表 2、因为t2是跟t1,t3都有关联查询的,这样的话我猜测对t1,t3的查询是不是可以并行执行,而如果使用t1,t3作为主表的话,是否会先跟t2生成中间表,是个串行的过程? 3、需要给t1加(a,c)索引,给t2加(c,a,b)索引。

    作者回复: 👍 很深入的思考哈 1. select * ,所以放整行;你说得对,select * 不是好习惯; 2. 第一次join后就筛选;第二次join再筛选; 新春快乐~

    2019-02-02
    2
    19
  • WL
    请教老师两个问题: 1. 通过主键索引找到的数据会会不会先在内存中查询, 如果没有再去磁盘查询? 2. 为什么在通过主键索引查询数据时, 符合条件的数据以单条数据的方式读到内存中而不是以一整个数据页的方式读到内存中?

    作者回复: 1. 通过普通索引也会,InnoDB的访问模式都是先内存,不在内存中,才到磁盘找; 2. 是以数据页的方式读到内存的,然后在从内存的这个数据页(默认16k)里面找到数据。

    2019-02-11
    2
    16
  • 涛哥哥
    老师,对于现在的固态硬盘,这样类似顺序读写的数据库优化,不就不起作用了啊?

    作者回复: 固态硬盘的顺序写还是比随机写快的

    2019-03-20
    13
  • TKbook
    BNL 算法效率低,建议你都尽量转成 BKA 算法。优化的方向就是给驱动表的关联字段加上索引; 老师最后总结的时候,这句话后面那句,应该是给被驱动表的关联字段加上索引吧。

    作者回复: 对的,👍细致 已经发起勘误,谢谢你哦,新年快乐

    2019-02-01
    11
  • dzkk
    老师,记得我之前看mysql的join是和版本有关系的,另外NLJ是一个统称,被分为了SNLJ(Simple Nested-Loop Join,5.5版本之前采用的,当被驱动表上没有索引的时候使用,该方法比较粗暴,所以后来通过BNLJ进行了优化)、INLJ(Index Nested-Loop Join,被驱动表上有索引)、BNLJ(Block Nested-Loop Join,被驱动表上没有索引),另外了解到mariadb是支持了hash join的Block Nested Loop Hash (BNLH) join,没有使用过,不知道效果怎么样。不知道我了解的信息对不对。

    作者回复: 你了解得比较全面哈 不过我怕在文章中写这么多概念,会看得晕。 实际上现在 Simple Nested-Loop Join 已经不会用了(太慢),有使用的就是 Index Nested-Loop Join 和 BKA优化哈。 MariaDB在优化器上做了很多工作,之前的文章本来也想介绍,后来发现得先把官方版本的说明白,然后我们可以在评论区扩展讨论。 BNLH 在MariaDB 5.3就引入了,流程跟我们“扩展-hash join”这段类似,对于等值join的效果还是很好的。

    2019-02-01
    11
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部