#### 8.2.1.12 阻止嵌套循环和批量密钥访问连接

[](<>)[](<>)[](<>)[](<>)

在 MySQL 中，可以使用批处理键访问 (BKA) 连接算法，该算法使用对连接表的索引访问和连接缓冲区。BKA 算法支持内连接、外连接和半连接操作，包括嵌套外连接。BKA 的好处包括由于更有效的表扫描而提高了连接性能。此外，以前仅用于内部联接的块嵌套循环 (BNL) 联接算法得到扩展，可用于外部联接和半联接操作，包括嵌套外部联接。

以下部分讨论作为原始 BNL 算法、扩展 BNL 算法和 BKA 算法扩展基础的连接缓冲区管理。有关半连接策略的信息，请参阅[第 8.2.2.1 节，“使用半连接转换优化 IN 和 EXISTS 子查询谓词”](semijoins.html)

-   [块嵌套循环和批量密钥访问算法的加入缓冲区管理](bnl-bka-optimization.html#join-buffer-management)

-   [用于外部连接和半连接的块嵌套循环算法](bnl-bka-optimization.html#bnl-optimization)

-   [批量密钥访问连接](bnl-bka-optimization.html#bka-optimization)

-   [块嵌套循环和批量密钥访问算法的优化器提示](bnl-bka-optimization.html#bnl-bka-optimizer-hints)

##### 块嵌套循环和批量密钥访问的加入缓冲区管理

算法

MySQL 不仅可以使用连接缓冲区来执行没有索引访问内表的内连接，还可以执行子查询展平后出现的外连接和半连接。此外，当对内部表进行索引访问时，可以有效地使用连接缓冲区。

连接缓冲区管理代码在存储感兴趣的行列的值时更有效地利用连接缓冲区空间：如果行列的值是`空值`，并且为任何值分配最小字节数[`VARCHAR`](char.html)类型。

该代码支持两种类型的缓冲区，常规的和增量的。假设连接缓冲区`B1`用于连接表`t1`和`t2`并且此操作的结果与表连接`t3`使用连接缓冲区`B2`：

-   常规连接缓冲区包含来自每个连接操作数的列。如果`B2`是一个常规的连接缓冲区，每一行*`r`*投入`B2`由一行的列组成*`r1`*从`B1`和匹配行的有趣列*`r2`*从表`t3`.

-   增量连接缓冲区仅包含第二个连接操作数生成的表行中的列。也就是说，它从第一个操作数缓冲区递增到一行。如果`B2`是一个增量连接缓冲区，它包含行的有趣列*`r2`*连同行的链接*`r1`*从`B1`.

    增量连接缓冲区始终相对于先前连接操作的连接缓冲区是增量的，因此第一个连接操作的缓冲区始终是常规缓冲区。在刚刚给出的示例中，缓冲区`B1`用于连接表`t1`和`t2`必须是常规缓冲区。

    用于连接操作的增量缓冲区的每一行仅包含要连接的表中一行中感兴趣的列。这些列通过对第一个连接操作数生成的表中匹配行的感兴趣列的引用进行扩充。增量缓冲区中的几行可以引用同一行*`r`*只要所有这些行与行匹配，其列就存储在先前的连接缓冲区中*`r`*.

    增量缓冲区可以减少从用于先前连接操作的缓冲区中复制列的频率。这节省了缓冲区空间，因为在一般情况下，第一个连接操作数产生的行可以与第二个连接操作数产生的几行匹配。没有必要从第一个操作数复制几行。由于复制时间的减少，增量缓冲区还可以节省处理时间。

    在 MySQL 8.0 中，[`block_nested_loop`](switchable-optimizations.html#optflag_block-nested-loop)国旗[`优化器开关`](server-system-variables.html#sysvar_optimizer_switch)系统变量的工作方式如下：

-   在 MySQL 8.0.20 之前，它控制优化器如何使用 Block Nested Loop 连接算法。

-   在 MySQL 8.0.18 及更高版本中，它还控制哈希连接的使用（参见[第 8.2.1.4 节，“哈希连接优化”](hash-joins.html)）。

-   从 MySQL 8.0.20 开始，该标志只控制哈希连接，不再支持块嵌套循环算法。

    这[`batched_key_access`](switchable-optimizations.html#optflag_batched-key-access)flag 控制优化器如何使用批量密钥访问连接算法。

    默认，[`block_nested_loop`](switchable-optimizations.html#optflag_block-nested-loop)是`在`和[`批量密钥访问`](switchable-optimizations.html#optflag_batched-key-access)是`关`看见[第8.9.2节，“可切换优化”](switchable-optimizations.html)。也可以应用优化器提示；看见[块嵌套循环和批处理密钥访问算法的优化器提示](bnl-bka-optimization.html#bnl-bka-optimizer-hints).

    有关半连接策略的信息，请参阅[第8.2.2.1节，“使用半连接转换优化IN和EXISTS子查询谓词”](semijoins.html)

##### 外连接和半连接的块嵌套循环算法

[](<>)[](<>)

MySQL BNL算法的最初实现被扩展为支持外部连接和半连接操作（后来被哈希连接算法取代；请参阅）[第8.2.1.4节，“哈希连接优化”](hash-joins.html)).

当使用联接缓冲区执行这些操作时，放入缓冲区的每一行都会提供一个匹配标志。

如果使用联接缓冲区执行外部联接操作，则会检查第二个操作数生成的表的每一行是否与联接缓冲区中的每一行匹配。当找到匹配项时，将形成一个新的扩展行（原始行加上第二个操作数中的列），并通过剩余的联接操作发送进一步的扩展。此外，将启用缓冲区中匹配行的匹配标志。检查完要联接的表的所有行后，将扫描联接缓冲区。缓冲区中未启用匹配标志的每一行都扩展为`无效的`补充(`无效的`第二个操作数中每列的值），并由剩余的联接操作发送以进行进一步扩展。

在MySQL 8.0中[`块嵌套循环`](switchable-optimizations.html#optflag_block-nested-loop)美国国旗[`优化器_开关`](server-system-variables.html#sysvar_optimizer_switch)系统变量的工作原理如下：

-   在MySQL 8.0.20之前，它控制优化器如何使用块嵌套循环联接算法。

-   在MySQL 8.0.18及更高版本中，它还控制哈希联接的使用（参见[第8.2.1.4节，“哈希连接优化”](hash-joins.html)).

-   从MySQL 8.0.20开始，该标志仅控制哈希连接，不再支持块嵌套循环算法。

    看见[第8.9.2节，“可切换优化”](switchable-optimizations.html)，以获取更多信息。也可以应用优化器提示；看见[块嵌套循环和批处理密钥访问算法的优化器提示](bnl-bka-optimization.html#bnl-bka-optimizer-hints).

    在里面[`解释`](explain.html)输出，当`额外的`值包含`使用连接缓冲区（块嵌套循环）`还有`类型`价值是[`全部的`](explain-output.html#jointype_all), [`指数`](explain-output.html#jointype_index)或[`范围`](explain-output.html#jointype_range).

    有关半联接策略的信息，请参阅[第8.2.2.1节，“使用半连接转换优化IN和EXISTS子查询谓词”](semijoins.html)

##### 批处理密钥访问连接

[](<>)[](<>)

MySQL实现了一种连接表的方法，称为批处理密钥访问（BKA）连接算法。当对第二个联接操作数生成的表进行索引访问时，可以应用BKA。与BNL join算法一样，BKA join算法使用联接缓冲区来累积联接操作的第一个操作数生成的行的有趣列。然后，BKA算法构建键来访问缓冲区中所有行要联接的表，并将这些键批量提交给数据库引擎进行索引查找。钥匙通过多量程读取（MRR）界面提交给发动机（参见[第8.2.1.11节，“多量程读取优化”](mrr-optimization.html))提交键后，MRR引擎函数以最佳方式在索引中执行查找，获取这些键找到的联接表的行，并开始向BKA联接算法提供匹配行。每个匹配行都与连接缓冲区中一行的引用耦合。

当使用BKA时[`加入缓冲区大小`](server-system-variables.html#sysvar_join_buffer_size)定义对存储引擎的每个请求中密钥的批量大小。缓冲区越大，对连接操作右侧表的顺序访问就越多，这可以显著提高性能。

对于要使用的BKA[`批量密钥访问`](switchable-optimizations.html#optflag_batched-key-access)美国国旗[`优化器_开关`](server-system-variables.html#sysvar_optimizer_switch)系统变量必须设置为`在…上`.BKA使用MRR，因此[`mrr`](switchable-optimizations.html#optflag_mrr)国旗也必须是`在…上`目前，MRR的成本估算过于悲观。因此，这也是必要的[`基于成本的mrr_`](switchable-optimizations.html#optflag_mrr-cost-based)成为`关`用于BKA。以下设置启用BKA：

```
mysql> SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
```

MRR功能的执行有两种情况：

-   第一种场景用于传统的基于磁盘的存储引擎，例如[`InnoDB`](innodb-storage-engine.html)和[`米萨姆`](myisam-storage-engine.html)。对于这些引擎，通常会立即将连接缓冲区中所有行的密钥提交到MRR接口。特定于引擎的MRR函数对提交的键执行索引查找，从中获取行ID（或主键），然后根据BKA算法的请求逐个获取所有这些选定行ID的行。每一行都返回一个关联引用，该引用允许访问联接缓冲区中匹配的行。MRR函数以最佳方式获取行：它们以行ID（主键）顺序获取。这提高了性能，因为读取是按磁盘顺序而不是随机顺序进行的。

-   第二种场景用于远程存储引擎，例如[`NDB`](mysql-cluster.html)。连接缓冲区中一部分行的密钥包及其关联由MySQL服务器（SQL节点）发送到MySQL群集数据节点。作为回报，SQL节点接收匹配行的一个包（或多个包）以及相应的关联。BKA join算法获取这些行并构建新的连接行。然后向数据节点发送一组新的键，并使用返回包中的行来构建新的连接行。该过程将继续，直到连接缓冲区中的最后一个键被发送到数据节点，并且SQL节点已接收并连接与这些键匹配的所有行。这提高了性能，因为SQL节点向数据节点发送的密钥承载包更少，这意味着它与数据节点之间执行连接操作的往返次数更少。

    在第一种情况下，连接缓冲区的一部分被保留用于存储由索引查找选择的行ID（主键），并作为参数传递给MRR函数。

    没有特殊的缓冲区来存储为join缓冲区中的行生成的键。相反，为缓冲区中的下一行生成密钥的函数作为参数传递给MRR函数。

    在里面[`解释`](explain.html)输出，当`额外的`值包含`使用连接缓冲区（批量密钥访问）`还有`类型`价值是[`裁判`](explain-output.html#jointype_ref)或[`eq_ref`](explain-output.html#jointype_eq_ref).

##### 块嵌套循环和批处理密钥访问算法的优化器提示

除了使用[`优化器_开关`](server-system-variables.html#sysvar_optimizer_switch)系统变量为了控制优化器在整个会话范围内使用BNL和BKA算法，MySQL支持优化器提示，以每语句为基础影响优化器。看见[第8.9.3节，“优化器提示”](optimizer-hints.html).

要使用BNL或BKA提示为外部联接的任何内部表启用联接缓冲，必须为外部联接的所有内部表启用联接缓冲。
