MySQL 的执行计划 Extra 列 Using join buffer (Block Nested Loop)
在 MySQL 的执行计划中,Using join buffer (Block Nested Loop)
表示在执行嵌套循环连接(Nested Loop Join)时,MySQL 使用了一种称为块嵌套循环(Block Nested Loop)的优化策略,这种策略会用到连接缓冲区(join buffer)。
具体解释
-
驱动表和被驱动表
- 驱动表:在嵌套循环中,首先处理的表。
- 被驱动表:驱动表中的每一行都会和它进行匹配查询的表。
-
为什么需要 Join Buffer?
当 MySQL 执行两表连接时,如果被驱动表没有合适的索引(例如ON
条件中未能使用索引),MySQL 无法直接通过索引快速找到匹配的记录。这种情况下,需要对被驱动表做全表扫描。为了减少与被驱动表之间的交互次数,MySQL 会将驱动表的部分数据加载到连接缓冲区(join buffer)中,然后对被驱动表逐行扫描,尝试匹配连接条件。
-
Block Nested Loop 的工作方式
块嵌套循环优化了传统的嵌套循环算法,通过减少驱动表与被驱动表交互的次数提高性能。执行过程如下:- Step 1:读取驱动表的一部分数据(多个数据块)放入
join buffer
。 - Step 2:对被驱动表进行全表扫描,将被驱动表中的每一行与
join buffer
中的数据进行比较,匹配连接条件。 - Step 3:匹配的结果保留,不匹配的丢弃。
- Step 4:如果驱动表尚未读取完毕,则继续读取下一部分数据,重复上述步骤。
- Step 1:读取驱动表的一部分数据(多个数据块)放入
-
性能影响
- 如果驱动表的数据量较大,
join buffer
可能会频繁写入和读取,从而造成性能瓶颈。 - 如果被驱动表的数据量较大(并且没有索引),需要频繁扫描,也会导致较大的 I/O 开销。
- 如果驱动表的数据量较大,
-
改进方法
- 给被驱动表加索引:索引能够显著提高连接查询的效率,避免全表扫描。
- 优化 SQL 语句的执行计划:通过调整表的连接顺序,让更小的数据表作为驱动表。
- 增加
join_buffer_size
:如果无法加索引,可以通过增大join_buffer_size
(默认为 262144 字节,256KB),减少缓冲区写入和读取的次数。
示例
假设有两个表:A
和 B
,需要执行以下查询:
SELECT *
FROM A
JOIN B
ON A.col1 = B.col2;
情况 1:B.col2
上有索引
MySQL 会直接使用索引查找匹配的 B
表记录,执行效率高。
情况 2:B.col2
上没有索引
- MySQL 将读取
A
表的部分数据块放入join buffer
。 - 然后对
B
表逐行扫描,尝试匹配join buffer
中的数据。 - 执行计划中会显示:
Using join buffer (Block Nested Loop)
。
Using join buffer (Block Nested Loop)
** 是一种优化策略,用于在连接查询时处理缺乏索引的被驱动表。尽管它优化了传统的嵌套循环,但性能仍然不及直接使用索引。优化这类查询的最佳方法是为连接条件字段建立合适的索引。