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