在 MySQL 中,可以使用 Batched Key Access (BKA) Join 算法,它使用对连接表的索引访问和连接缓冲区。BKA 算法支持内连接、外连接和半连接操作,包括嵌套外连接。BKA 的好处包括由于更有效的表扫描而提高了连接性能。此外,以前仅用于内部连接的块嵌套循环 (BNL) 连接算法得到扩展,可用于外部连接和半连接操作,包括嵌套外部连接。
以下各节讨论作为原始 BNL 算法扩展、扩展 BNL 算法和 BKA 算法基础的连接缓冲区管理。有关半连接策略的信息,请参阅第 8.2.2.1 节,“使用半连接转换优化子查询、派生表和视图引用”
MySQL 不仅可以使用连接缓冲区来执行不对内表进行索引访问的内部连接,还可以执行子查询展平后出现的外部连接和半连接。此外,当存在对内表的索引访问时,可以有效地使用连接缓冲区。
连接缓冲区管理代码在存储感兴趣的行列的值时稍微更有效地利用连接缓冲区空间:如果行列的值为 ,则不会在缓冲区中分配额外
NULL
的字节,并且为任何值分配最小字节数VARCHAR
类型
。
该代码支持两种类型的缓冲区,常规缓冲区和增量缓冲区。假设使用连接缓冲区B1
来连接表t1
,
t2
并且此操作的结果t3
使用连接缓冲区
与表连接B2
:
常规连接缓冲区包含来自每个连接操作数的列。如果
B2
是常规连接缓冲区,则r
放入B2
的每一行都由来自 table 的一行的列和r1
来自 tableB1
的匹配行的有趣列组成。r2
t3
增量连接缓冲区仅包含来自第二个连接操作数生成的表行的列。也就是说,它从第一个操作数缓冲区开始递增到一行。如果
B2
是增量连接缓冲区,它包含该行的有趣列以及到该行的r2
链接 。r1
B1
增量连接缓冲区始终相对于来自较早连接操作的连接缓冲区是增量的,因此来自第一个连接操作的缓冲区始终是常规缓冲区。在刚刚给出的示例中,B1
用于连接表t1
的
缓冲区t2
必须是常规缓冲区。
用于连接操作的增量缓冲区的每一行仅包含要连接的表中一行的有趣列。这些列通过对第一个连接操作数生成的表中匹配行的有趣列的引用来扩充。增量缓冲区中的几行可以引用同一行
r
,其列存储在先前的连接缓冲区中,只要所有这些行都与 row 匹配
r
。
增量缓冲区可以减少从用于先前连接操作的缓冲区中复制列的频率。这节省了缓冲区空间,因为在一般情况下,第一个连接操作数生成的一行可以与第二个连接操作数生成的多行相匹配。没有必要从第一个操作数复制一行的多个副本。由于复制时间的减少,增量缓冲区还可以节省处理时间。
系统变量的block_nested_loop
和
batched_key_access
标志optimizer_switch
控制优化器如何使用 Block Nested-Loop 和 Batched Key Access 连接算法。默认情况下,block_nested_loop
是on
和
batched_key_access
是
off
。请参阅
第 8.9.2 节,“可切换优化”。也可以应用优化器提示;请参阅
块嵌套循环和批量密钥访问算法的优化器提示。
有关半连接策略的信息,请参阅 第 8.2.2.1 节,“使用半连接转换优化子查询、派生表和视图引用”
MySQL BNL 算法的原始实现被扩展以支持外连接和半连接操作。
当使用连接缓冲区执行这些操作时,放入缓冲区的每一行都会提供一个匹配标志。
如果使用连接缓冲区执行外部连接操作,则检查由第二个操作数生成的表的每一行是否与连接缓冲区中的每一行匹配。找到匹配项后,将形成一个新的扩展行(原始行加上来自第二个操作数的列)并发送给剩余的连接操作以进行进一步扩展。此外,启用缓冲区中匹配行的匹配标志。检查完要连接的表的所有行后,扫描连接缓冲区。缓冲区中未启用其匹配标志的每一行都由NULL
补码扩展(NULL
第二个操作数中每列的值)并发送给剩余的连接操作以进行进一步扩展。
系统变量的block_nested_loop
标志optimizer_switch
控制优化器如何使用 Block Nested-Loop 算法。默认情况下,
block_nested_loop
是
on
. 请参阅
第 8.9.2 节,“可切换优化”。也可以应用优化器提示;请参阅
块嵌套循环和批量密钥访问算法的优化器提示。
在输出中,当
值包含且值为
、
或
EXPLAIN
时,表示对表使用 BNL 。
Extra
Using join buffer (Block Nested
Loop)
type
ALL
index
range
一些涉及一个或多个子查询与一个或多个左连接的组合的情况,特别是那些返回许多行的情况,可能会使用 BNL,即使在这种情况下它并不理想。这是一个已知问题,已在 MySQL 8.0 中修复。如果升级 MySQL 对您来说不是立即可行的,您可能希望同时通过设置
optimizer_switch='block_nested_loop=off'
或使用NO_BNL
优化器提示来禁用 BNL,让优化器选择一个更好的计划,使用一个或多个索引提示(参见
第 8.9.4 节,“索引提示”)或这些的某种组合,以提高此类查询的性能。
有关半连接策略的信息,请参阅 第 8.2.2.1 节,“使用半连接转换优化子查询、派生表和视图引用”
MySQL 实现了一种连接表的方法,称为 Batched Key Access (BKA) 连接算法。当对第二个连接操作数生成的表有索引访问时,可以应用 BKA。与 BNL 连接算法一样,BKA 连接算法使用连接缓冲区来累积连接操作的第一个操作数生成的行的有趣列。然后 BKA 算法为缓冲区中的所有行构建键以访问要连接的表,并将这些键批量提交给数据库引擎以进行索引查找。密钥通过多范围读取 (MRR) 接口提交给引擎(请参阅 第 8.2.1.10 节,“多范围读取优化”). 提交键后,MRR 引擎函数以最佳方式在索引中执行查找,获取由这些键找到的连接表的行,并开始为 BKA 连接算法提供匹配的行。每个匹配行都与连接缓冲区中一行的引用相耦合。
当使用 BKA 时, 的值
join_buffer_size
定义了在对存储引擎的每个请求中批量密钥的大小。缓冲区越大,对连接操作的右侧表进行的顺序访问就越多,这可以显着提高性能。
要使用 BKA,
必须将系统变量的batched_key_access
标志设置为。BKA 使用 MRR,所以标志也必须是. 目前,对 MRR 的成本估算过于悲观。因此,也有必要
使用
BKA。以下设置启用 BKA:
optimizer_switch
on
mrr
on
mrr_cost_based
off
mysql> SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
MRR 函数执行的场景有两种:
第一种情况用于传统的基于磁盘的存储引擎,例如
InnoDB
和MyISAM
. 对于这些引擎,通常将连接缓冲区中所有行的键一次提交给 MRR 接口。引擎特定的 MRR 函数对提交的键执行索引查找,从中获取行 ID(或主键),然后根据 BKA 算法的请求为所有这些选定的行 ID 逐一获取行。每行都返回一个关联引用,该引用允许访问连接缓冲区中的匹配行。MRR 函数以最佳方式提取行:它们按行 ID(主键)顺序提取。这提高了性能,因为读取是按磁盘顺序而不是随机顺序进行的。第二种场景用于远程存储引擎,例如
NDB
. 来自连接缓冲区的部分行的键包及其关联由 MySQL 服务器(SQL 节点)发送到 NDB Cluster 数据节点。作为回报,SQL 节点接收到匹配行的一个包(或多个包)以及相应的关联。BKA 连接算法获取这些行并构建新的连接行。然后一组新的键被发送到数据节点,返回包中的行用于构建新的连接行。该过程一直持续到连接缓冲区中的最后一个键被发送到数据节点,并且 SQL 节点已经接收并连接了与这些键匹配的所有行。
With the first scenario, a portion of the join buffer is reserved to store row IDs (primary keys) selected by index lookups and passed as a parameter to the MRR functions.
There is no special buffer to store keys built for rows from the join buffer. Instead, a function that builds the key for the next row in the buffer is passed as a parameter to the MRR functions.
In EXPLAIN
output, use of BKA
for a table is signified when the Extra
value contains Using join buffer (Batched Key
Access)
and the type
value is
ref
or
eq_ref
.
除了使用
optimizer_switch
系统变量来控制优化器在会话范围内使用 BNL 和 BKA 算法之外,MySQL 还支持优化器提示以在每个语句的基础上影响优化器。请参阅
第 8.9.3 节,“优化器提示”。
要使用 BNL 或 BKA 提示为外连接的任何内表启用连接缓冲,必须为外连接的所有内表启用连接缓冲。