mysql索引联合查询底层Nested-Loop Join和Block-Nested-Loop Join算法

首先介绍这两种算法之前,先介绍一个mysql关联表优化的原则,即小表驱动大表(这个后续会解释)

1.Nested-Loop Join(NLJ):

    我现在先创建两张表,emp_a里面有5条数据,emp_b里面有40条数据(数据生成太麻烦了,有兴趣的可以生成多一点的数据去试验一下),name字段设索引,age字段不设索引

对这两张表做关联查询查看执行计划

    

我们会发现mysql会先扫描emp_b表,并且是全表扫描。

来稍微解释一下,当你两表关联查询的时候,关联字段是索引的情况下,mysql底层会选择NLJ算法,也可以从执行计划的Extra字段中看出来,一般情况下Extra字段里面没有出现Using join buff,则表示用的就是NLJ算法

我们来看一下上面这条sql执行的大致流程:

1.先从emp_b表中读取一行数据

2.取出关联字段name,去emp_a表中去查询

3.从emp_a表中查找出满足条件的行,同emp_b表中的数据合并,返回客户端

4.从emp_b表中读取下一条数据,重复上面的操作

我们来看一下上面的查询扫描的数据行数:

    首先,emp_b表必定是全表扫描,因为他需要遍历读取,那么首先是扫描了emp_b表的5行,然后emp_a表根据关联的name字段去扫描行,name为索引字段,那么emp_a表这边也大概是扫描了5行,总共就是扫描了5+5=10行(上面执行计划的rows=7不代表实际扫描就是7行,只代表大约扫描了7行)

2.Block-Nested-Loop Join

我们再来看一下关联查询,关联字段不是索引的执行计划

通过执行计划也可以看出来Extra字段会显示Using join buffer(Block Nested Loop)

我们来看一下这条sql执行的大致流程:

1.把emp_b的所有数据先放到一个join buffer(缓存)里面

2.从emp_a表中取出每一条数据同join buffer中的数据做对比

3.找到比对上的数据,返回客户端

我们来看一下上面的查询扫描的数据行数:

    首先,emp_b表必定是全表扫码,因为他需要全部放到join buffer里面,那么首先是扫描了emp_b的5行,然后每次扫描emp_a中的每一行去join buffer比对,所以对emp_a也需要进行全表扫码,即总共需要扫描5+40 = 45行(上面执行计划39行也是大约,而且我后来特地看了下库里面有一条数据被我不小心删掉了,所以实际表里面确实是只有39行数据,但是这个39行只能用来做参考,不能代表实际扫描行数)。

这边还有一个点,就是数据在join buffer里面都是无序的,所以emp_a中的数据在join buffer中需要与emp_b的数据比对,每次匹配都需要做5次判断,所以在内存中的判断次数是5*40=200次.

这个判断的200次和扫描的200次是有本质区别的,消耗同200次的扫描比起来可以忽略不计;

那么为什么是用小表驱动大表呢?

Nested-Loop Join先扫描大表的话,就要先扫描40次了

Block-Nested_Loop Join先把大表的数据放入join buffer,就会导致内存占用过大。

个人大致理解就是这样,如果有写的不对的,欢迎一起讨论交流,谢谢!

猜你喜欢

转载自blog.csdn.net/m0_37948078/article/details/107867361