Documentation Home
MySQL 8.3 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 40.8Mb
PDF (A4) - 40.9Mb
Man Pages (TGZ) - 294.0Kb
Man Pages (Zip) - 409.0Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb
Excerpts from this Manual

MySQL 8.3 Reference Manual  /  ...  /  Nested-Loop Join Algorithms

10.2.1.7 嵌套循环连接算法

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

嵌套循环连接算法

简单的嵌套循环连接(NLJ)算法从第一个表中读取行,一次一行,将每行传递给嵌套循环,以处理下一个要连接的表。这个过程将重复执行,直到所有表都被连接。

假设要执行三个表 t1t2t3 的连接,使用以下连接类型:

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)连接算法使用外部循环读取的行缓冲,以减少内部循环中表的读取次数。例如,如果将 10 行读取到缓冲区,并将缓冲区传递给下一个内部循环,那么每个内部循环读取的行都可以与缓冲区中的所有 10 行进行比较。这将大大减少内部表的读取次数。

MySQL 使用哈希连接优化来执行等值连接,当无法使用索引时。

MySQL 连接缓冲具有以下特征:

  • 连接缓冲可以在连接类型为 ALLindex(即没有可能的键可以使用,分别对数据或索引行进行全扫描)时使用,也可以在 range 连接类型时使用。缓冲也适用于外连接,如 第 10.2.1.12 节“块嵌套循环和批量键访问连接” 中所述。

  • 即使是 ALLindex 类型,连接缓冲也不会分配给第一个非常量表。

  • 只有连接中感兴趣的列被存储在连接缓冲中,而不是整个行。

  • 系统变量 join_buffer_size 确定了每个连接缓冲的大小。

  • 每个可以缓冲的连接都分配一个缓冲区,因此一个查询可能使用多个连接缓冲。

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

对于之前描述的 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 是连接缓冲中存储的每个 t1t2 组合的大小,C 是缓冲区中的组合数,那么表 t3 的扫描次数为:

(S * C)/join_buffer_size + 1

随着 join_buffer_size 的值增加,表 t3 的扫描次数将减少,直到 join_buffer_size 足够大以容纳所有之前的行组合。在那时,不再通过增加 join_buffer_size 的值来提高速度。