MySQL多表关联之Block Nested-Loop Join

本文转载自:MySQL多表关联之Block Nested-Loop Join

一、背景

从事DBA工作两年多以来,经常会遇到开发上线的SQL中含有多表关联join的查询,我自己本身也是比较抗拒的,很多DBA一般会建议开发拆分SQL,避免join带来的性能问题。但是我始终认为,任何事物存在必然有它的理由,不能全盘否定它!在5.5版本之前,MySQL只支持一种表间关联方式,也就是嵌套循环(Nested Loop)。如果关联的表数据量很大,那么join关联的时间会很长。在5.5版本以后,MySQL引入了BNL算法来优化嵌套循环。

 

二、Nested Loop Join算法

NLJ算法:将外层表的结果集作为循环的基础数据,然后循环从该结果集每次一条获取数据作为下一个表的过滤条件去查询数据,然后合并结果。如果有多个表join,那么应该将前面的表的结果集作为循环数据,取结果集中的每一行再到下一个表中继续进行循环匹配,获取结果集并返回给客户端。该算法的伪代码为:

1for each row in t1 matching range {

2  for each row in t2 matching reference key {

3     for each row in t3 {

4      if row satisfies join conditions,

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

5      send to client

6    }

7  }

8 }

 

普通的Nested-Loop Join算法一次只能将一行数据传入内存循环,所以外层循环结果集有多少行,那么内存循环就要执行多少次。

 

三、Block Nested-Loop Join算法

BNL算法原理:将外层循环的行/结果集存入join buffer,内存循环的每一行数据与整个buffer中的记录做比较,可以减少内层循环的扫描次数

 

 

举个简单的例子:外层循环结果集有1000行数据,使用NLJ算法需要扫描内层表1000次,但如果使用BNL算法,则先取出外层表结果集的100行存放到join buffer, 然后用内层表的每一行数据去和这100行结果集做比较,可以一次性与100行数据进行比较,这样内层表其实只需要循环1000/100=10次,减少了9/10。

 

伪代码如下:

1for each row in t1 matching range {

2   for each row in t2 matching reference key {

3    store used columns from t1, t2 in join buffer

4    if buffer is full {

5      for each row in t3 {

6         for each t1, t2 combination in join buffer {

7          if row satisfies join conditions,

8          send to client

9        }

10       }

11      empty buffer

12    }

13  }

14}

15

16

17if buffer is not empty {

18   for each row in t3 {

19    for each t1, t2 combination in join buffer {

20      if row satisfies join conditions,

21      send to client

22     }

23  }

24}

 

 我们用一个示意图来简明阐述一下:

MySQL多表关联之Block <wbr>Nested-Loop <wbr>Join
 

我们从这个图可以看到,BNL算法把t1和t2的结果集存放到join buffer中(t1表和t2表的关联,仍然是取出t1表的每一行记录和t2表的每一行记录进行匹配,t1表仍然是full scan全表扫描),而不是每次从t1表取出一条记录和t2表进行能过匹配得出结果集,就马上和t3进行关联。

 

对于放入到join buffer的列,是指所有参与查询的列,而不是只有join的列。如下面的这个查询:

SELECT a.col3 FROM a,b

  WHERE a.col1 = b.col2

  AND a.col2 > …. AND b.col2 = …

 

上述SQL语句外表是a,内表是b,那么存放在join buffer的列是所有参与查询的列,这里是:(a.col1、a.col2、a.col3).

 

我们可以从explain的sql执行计划中看到Extra有“using join buffer”,则说明mysql使用了join buffer来对sql进行优化.

 

使用join buffer的要点:

1)、join buffer size变量大小决定了buffer的大小

2)、只有在join类型为all 或者index或者range的时候,才可以使用join buffer(也就是说explain执行计划中,type为all或者index或者range的时候,才会出现Using join buffer)。如:

id1

select_typeSIMPLE

table:  c

typeALL

possible_keysNULL

keyNULL

key_lenNULL

refNULL

rows96

ExtraUsing whereUsing join buffer

 

这里type: ALL,所以可以使用join buffer.

3)、在join之前就会分配join buffer,在query执行完毕之后,立即释放buffer

 

对于Block Nested-Loop Join的算法,希望有哪位朋友有更深入了解的,可以一起交流一下~~~

猜你喜欢

转载自blog.csdn.net/qq_34291570/article/details/92974736