MySQL查询优化之NLJ和BNL

原文地址:https://dev.mysql.com/doc/refman/5.7/en/nested-loop-joins.html

译文:

8.2.1.6 嵌套循环连接算法

MySQL使用嵌套循环算法或其变体执行表之间的连接。

嵌套循环连接(NLJ)算法

一个简单的nested-loop join (NLJ)算法每次读取一个循环中的第一个表中的行,将每一行传递给一个嵌套循环,该循环处理连接中的下一个表。只要还有表需要连接,这个过程就会重复多次。

假设三个表t1、t2和t3之间的连接将使用以下连接类型执行:

Table   Join Type
t1      range
t2      ref
t3      ALL

如果使用了NLJ算法,连接会被处理成如下形式:

for each row in t1 matching range {
  for each row in t2 matching reference key {
    for each row in t3 {
      if row satisfies join conditions, send to client
    }
  }
}

由于NLJ算法每次将一行从外部循环传递到内部循环,因此它通常会多次读取在内部循环中处理的表。

块嵌套循环连接(BNL)算法

BNL算法通过使用外部循环中读取行的缓冲,来减少内部循环中的表必须被读取的次数。例如,如果将10行读入缓冲区,并将缓冲区传递给下一个内部循环,则可以将内部循环中读取的每一行与缓冲区中的所有10行进行比较。这样可以将必须读取内部表的次数减少一个数量级。

MySQL连接缓冲具有如下特性:

    1)当连接类型为all或index(换句话说,当不能使用可能的索引键时,将分别对数据行或索引行进行全面扫描)或range时,可以使用连接缓冲。缓冲区的使用同样适用于外链接,可以参考:Section 8.2.1.11, “Block Nested-Loop and Batched Key Access Joins”

    2)永远不会为第一个非常量表分配连接缓冲区,即使连接是all类型或index类型;

    3)只有与联接相关的列存储在联接缓冲区中,而不是所有的行;

    4)系统变量join_buffer_size决定了每个用于处理查询的连接缓冲区的大小;

    5)每个可以缓冲的连接都会被分配一个缓冲区,所以可能使用多个连接缓冲区处理给定的查询;

    6)连接缓冲区在执行连接之前分配,在查询完成后释放。

例如对于前文使用NLJ算法(没有缓冲区)的连接,使用缓冲后连接被处理成如下形式:

for each row in t1 matching range {
  for each row in t2 matching reference key {
    store used columns from t1, t2 in join buffer
    if buffer is full {
      for each row in t3 {
        for each t1, t2 combination in join buffer {
          if row satisfies join conditions, send to client
        }
      }
      empty join buffer
    }
  }
}

if buffer is not empty {
  for each row in t3 {
    for each t1, t2 combination in join buffer {
      if row satisfies join conditions, send to client
    }
  }
}

如果S是在存储在连接缓冲区中的t1和t2组合的大小,C为缓冲区中组合的个数,则扫描表t3的次数为:

(S * C)/join_buffer_size + 1

t3表扫描的次数随着join_buffer_size的值的增加而减少,直到join_buffer_size足够大以容纳以前所有的行组合时为止。在这种情况下,使join_buffer_size变大并不能提高速度。

PS:由于水平有限,译文中难免存在谬误,欢迎批评指正。

猜你喜欢

转载自blog.csdn.net/qq_41080850/article/details/85257945