1. Index Nested-Loop Join
概念解释:
假设有t1,t2两张表,在join连接的时候,t1表驱动t2表,t1走的全部扫描,t2表使用了索引,
则这个时候join就使用了“index nested-loop join”算法,简称:NLJ。
NLJ执行的流程如下:

2. Block Nested-Loop Join
基于上面t1表和t2表,join连接,t1表和t2表都没有命中索引,都是走的全部扫描。这个
时候Join使用的是“Block Nested-Loop Join”算法,简称:BNL。
BNL算法的流程如下:

在explain分析语句的时候,extra附加信息中会出现“Using join buffer (Block Nested Loop)”。
join_buffer是由参数join_buffer_size决定的。默认是256k。如果放不下表 t1
的所有数据话,策略很简单,就是分段放。
流程大概是:
取t1表的数据,放入join_buffer中,如果join buffer满了,就扫描t2表的数据,跟join buffer
的数据进行对比,满足join条件的作为结果集返回;清空join buffer,继续上面的过程。
能否使用Join?
1. 如果可以使用 Index Nested-Loop Join 算法,也就是说可以用上被驱动表上的索引,
其实是没问题的;
2. 如果使用 Block Nested-Loop Join 算法,扫描行数就会过多。尤其是在大表上的 join
操作,这样可能要扫描被驱动表很多次,会占用大量的系统资源。所以这种 join 尽量不
要用。
如果要使用 join,应该选择大表做驱动表还是选择小表做驱动表?
1. 如果是 Index Nested-Loop Join 算法,应该选择小表做驱动表;
2. 如果是 Block Nested-Loop Join 算法:
在 join_buffer_size 足够大的时候,是一样的;
在 join_buffer_size 不够大的时候(这种情况更常见),应该选择小表做驱动表。
这个问题的结论就是,总是应该使用小表做驱动表。
注意:join慢的时候,尽量跳大join_buffer_size的值。
优化Join
1. 优化NLJ算法
Multi-Range Read 优化算法 (MRR)。这个优化的主要目的是尽量使用顺序读盘。
如果随着辅助索引(二级索引) a 的值递增顺序查询的话,主键索引id 的值就变成随机的,
那么就会出现随机访问,性能相对较差。虽然“按行查”这个机制不能改,但是调整查询的顺序,
还是能够加速的。这就是 MRR 优化的设计思路。
加入了MRR优化的执行流程如下:
1. 根据索引 a,定位到满足条件的记录,将 id 值放入 read_rnd_buffer 中 ;
2. 将 read_rnd_buffer 中的 id 进行递增排序;
3. 排序后的 id 数组,依次到主键 id 索引中查记录,并作为结果返回。
read_rnd_buffer 的大小是由 read_rnd_buffer_size 参数控制的。
启用MRR算法:
set optimizer_switch="mrr_cost_based=off"
Batched Key Access (BKA) 算法
MySQL 在 5.6 引入的,是对 NLJ 算法的优化。
NLJ 算法优化后的 BKA 算法的流程如下:

启用 BKA 算法:
set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
前两个参数的作用是要启用 MRR。这么做的原因是,BKA 算法的优化要依赖于MRR。
BNL 算法的优化
优化的常见做法是,给被驱动表的 join 字段加上索引,把 BNL 算法转成 BKA 算法。
但是如果无法在被驱动表上加索引,那么:
考虑使用临时表,使用临时表的大致思路是:
1. 把表 t2 中满足条件的数据放在临时表 tmp_t 中;
2. 为了让 join 使用 BKA 算法,给临时表 tmp_t 的字段 b 加上索引;
3. 让表 t1 和 tmp_t 做 join 操作。
SQL语句为:
create temporary table temp_t(id int primary key, a int, b int, index(b))engine=innodb;
insert into temp_t select * from t2 where b>=1 and b<=2000;
select * from t1 join temp_t on (t1.b=temp_t.b);
总体来看,不论是在原表上加索引,还是用有索引的临时表,我们的思路都是让 join 语句
能够用上被驱动表上的索引,来触发 BKA 算法,提升查询性能。
上面如果不适用临时表进行优化,那么还有其他方式进行优化?
我们可以自己实现在业务端。实现流程大致如下:
1. select * from t1;取得表 t1 的全部 1000 行数据,在业务端存入一个 hash 结构,
比如 C++ 里的 set、PHP 的数组这样的数据结构。
2. select * from t2 where b>=1 and b<=2000; 获取表 t2 中满足条件的 2000 行
数据。
3. 把这 2000 行数据,一行一行地取到业务端,到 hash 结构的数据表中寻找匹配的数
据。满足匹配的条件的这行数据,就作为结果集的一行。
关于临时表的几个问题:
1. binlog_format=row,那么跟临时表有关的语句,就不会记录到binlog 里。只在
binlog_format=statment/mixed 的时候,binlog 中才会记录临时表的操作。
2. 在使用临时表的时候,最后最好要写上 DROP TEMPORARY TABLE,删除临时表
3. 临时表只对本session会话可见,对其他的session不可见。