在 MySQL 中,批量键访问(BKA)连接算法可用,使用索引访问连接表和连接缓冲区。BKA 算法支持内连接、外连接和半连接操作,包括嵌套外连接。BKA 的优点包括由于表扫描效率提高而提高连接性能。此外,块嵌套循环(BNL)连接算法以前仅用于内连接,现在也可以用于外连接和半连接操作,包括嵌套外连接。
以下部分讨论了连接缓冲区管理,它是 BNL 算法扩展和 BKA 算法的基础。有关半连接策略的信息,请参阅 使用半连接变换优化 IN 和 EXISTS 子查询谓词
MySQL 可以使用连接缓冲区来执行不仅是内连接,还有外连接和半连接,这些连接在子查询展平后出现。此外,当有索引访问内表时,连接缓冲区也可以有效地使用。
连接缓冲区管理代码可以更高效地利用连接缓冲区空间来存储有趣行列的值:如果行列的值为 NULL
,则不分配任何字节,并且为 VARCHAR
类型的任何值分配最少的字节。
代码支持两种类型的缓冲区,regular 和 incremental。假设使用连接缓冲区 B1
连接表 t1
和 t2
,然后使用连接缓冲区 B2
将结果与表 t3
连接:
-
普通的连接缓冲区包含每个连接操作数的列。如果
B2
是一个普通的连接缓冲区,每行r
放入B2
由来自B1
的行r1
的列和来自表t3
的匹配行r2
的有趣列组成。 -
增量连接缓冲区仅包含来自第二个连接操作数的表的行的列。也就是说,它是相对于第一个操作数缓冲区的增量。如果
B2
是一个增量连接缓冲区,它包含来自表t3
的行r2
的有趣列,以及指向来自B1
的行r1
的链接。
增量连接缓冲区总是相对于早期连接操作的缓冲区的增量,因此第一个连接操作的缓冲区总是一个普通缓冲区。在上面的示例中,用于连接表 t1
和 t2
的缓冲区 B1
必须是一个普通缓冲区。
每行增量缓冲区用于连接操作仅包含来自要连接的表的行的有趣列。这些列与来自第一个连接操作数的表的匹配行的有趣列一起存储在之前的连接缓冲区中。多行增量缓冲区可以引用同一行 r
,只要这些行都匹配行 r
。
增量缓冲区使得之前连接操作的缓冲区中的列复制次数减少,从而节省缓冲区空间,因为在一般情况下,第一个连接操作数的行可以匹配第二个连接操作数的多行。因此,不需要复制第一个操作数的行多次。增量缓冲区还可以减少处理时间,因为复制时间减少了。
系统变量 optimizer_switch
的 block_nested_loop
标志控制哈希连接。
系统变量 optimizer_switch
的 batched_key_access
标志控制优化器如何使用批量键访问连接算法。
默认情况下,block_nested_loop
是 on
,而 batched_key_access
是 off
。请参阅 第 10.9.2 节,“可切换优化”。优化器提示也可以应用;请参阅 Block Nested-Loop 和批量键访问算法的优化器提示。
有关半连接策略的信息,请参阅 使用半连接变换优化 IN 和 EXISTS 子查询谓词
MySQL BNL 算法的原始实现被扩展以支持外连接和半连接操作(后来被哈希连接算法所取代;见 第 10.2.1.4 节,“哈希连接优化”)。
当这些操作使用连接缓冲区执行时,每行放入缓冲区的行都提供了一个匹配标志。
如果外连接操作使用连接缓冲区执行,对第二个操作数的每行都检查是否与缓冲区中的每行匹配。当找到匹配时,形成一个扩展的行(原始行加上第二个操作数的列)并发送到剩余的连接操作中。此外,缓冲区中匹配行的匹配标志也被启用。在检查完第二个操作数的所有行后,扫描连接缓冲区。缓冲区中每行没有启用匹配标志的行都被扩展为 NULL 补充(第二个操作数的每列的 NULL 值)并发送到剩余的连接操作中。
系统变量 optimizer_switch
的 block_nested_loop
标志控制哈希连接。
请参阅 第 10.9.2 节,“可切换优化”,以获取更多信息。优化器提示也可以应用;见 Optimizer Hints for Block Nested-Loop and Batched Key Access Algorithms。
在 EXPLAIN
输出中,BNL 的使用在表中被标志为 Extra
值包含 Using join buffer (Block Nested Loop)
,并且 type
值是 ALL
, index
, 或 range
。
有关半连接策略的信息,请参阅 使用半连接变换优化 IN 和 EXISTS 子查询谓词
MySQL 实现了一种称为批量键访问(BKA)连接算法的表连接方法。当第二个连接操作数的表可以通过索引访问时,可以应用 BKA。像 BNL 连接算法一样,BKA 连接算法使用连接缓冲区来累积连接操作的第一个操作数产生的有趣列的行。然后,BKA 算法构建访问要连接的表的键,并将这些键批量提交给数据库引擎以进行索引查找。这些键通过多范围读取(MRR)接口(见 第 10.2.1.11 节,“多范围读取优化”)提交给引擎。引擎函数在索引中执行查找,以优化方式获取匹配的行,并将其馈送给 BKA 连接算法。每个匹配行都与连接缓冲区中的行引用相关联。
当使用 BKA 时,join_buffer_size
的值定义了每个请求中批量键的大小。缓冲区越大,对右侧表的顺序访问越多,从而可以显著提高性能。
要使用 BKA,batched_key_access
标志必须设置为 on
。BKA 使用 MRR,因此 mrr
标志也必须设置为 on
。目前,MRR 的成本估算太保守。因此,mrr_cost_based
也必须设置为 off
才能使用 BKA。以下设置启用 BKA:
mysql> SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
MRR 函数有两种执行场景:
-
第一种场景用于传统的基于磁盘的存储引擎,如
InnoDB
和MyISAM
。对于这些引擎,通常将连接缓冲区中的所有行的键提交给 MRR 接口一次。引擎特定的 MRR 函数执行索引查找,获取行 ID(或主键),然后逐个请求从 BKA 算法中获取这些选定的行 ID。每行都返回一个关联引用,以便访问连接缓冲区中的匹配行。这些行按照行 ID(主键)顺序获取,这提高了性能,因为读取是按照磁盘顺序而不是随机顺序。 -
第二种场景用于远程存储引擎,如
NDB
。MySQL 服务器(SQL 节点)将一组键和关联发送到 MySQL 集群数据节点,以获取 join 缓冲区的一部分行。然后,SQL 节点从数据节点接收匹配的行,并使用这些行构建新的连接行。然后,新的键被发送到数据节点,直到最后一个键从 join 缓冲区被发送到数据节点,并且 SQL 节点已经收到了所有匹配的行。这提高了性能,因为少量的键承载包被发送到数据节点,减少了 SQL 节点和数据节点之间的往返次数以执行连接操作。
在第一种场景中,join 缓冲区的一部分被保留来存储通过索引查找选择的行 ID(主键),并作为参数传递给 MRR 函数。
没有特殊的缓冲区来存储从 join 缓冲区构建的键。相反,一个构建下一个缓冲区行的键的函数被作为参数传递给 MRR 函数。
在 EXPLAIN
输出中,当 Extra 值包含 Using join buffer (Batched Key Access)
并且 type 值是 ref
或 eq_ref
时,表示 BKA 用于该表。
除了使用 optimizer_switch
系统变量来控制优化器对 BNL 和 BKA 算法的使用外,MySQL 还支持优化器提示来影响优化器的行为。请参阅 第 10.9.3 节,“优化器提示”。
要使用 BNL 或 BKA 提示来启用外连接的任何内部表的连接缓冲,必须为外连接的所有内部表启用连接缓冲。