这两个表都有一个主键索引 id 和一个索引 a,字段 b 上无索引。t1 100行,t2 1000行
CREATE TABLE `t2` ( `id` int(11) NOT NULL, `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `a` (`a`)) ENGINE=InnoDB;
create table t1 like t2;
1.Index Nested-Loop Join
select * from t1 straight_join t2 on (t1.a=t2.a);
STRAIGHT_JOIN只适用于内连接,因为left join、right join已经知道了哪个表作为驱动表,哪个表作为被驱动表,比如left join就是以左表为驱动表,right join反之,而STRAIGHT_JOIN就是在内连接中使用,而强制使用左表来当驱动表,所以这个特性可以用于一些调优,强制改变mysql的优化器选择的执行计划
在这个语句里,STRAIGHT_JOIN 指定左表为驱动表,t1 是驱动表,t2 是被驱动表。
执行流程是这样的:
从表 t1 中读入一行数据 R;
从数据行 R 中,取出 a 字段到表 t2 里去查找;
取出表 t2 中满足条件的行,跟 R 组成一行,作为结果集的一部分;
重复执行步骤 1 到 3,直到表 t1 的末尾循环结束。
这个 join 语句执行过程中,驱动表是走全表扫描,而被驱动表(存在索引)是走树搜索。
2.Simple Nested-Loop Join
select * from t1 straight_join t2 on (t1.a=t2.b);
表 t2 的字段 b 上没有索引,因此再用图 2 的执行流程时,每次到 t2 去匹配的时候,就要做一次全表扫描。
3.Block Nested-Loop Join
被驱动表上没有可用的索引,算法的流程是这样的:把表 t1 的数据读入线程内存 join_buffer 中,由于我们这个语句中写的是 select *,因此是把整个表 t1 放入了内存;扫描表 t2,把表 t2 中的每一行取出来,跟 join_buffer 中的数据做对比,满足 join 条件的,作为结果集的一部分返回。
在这个过程中,对表 t1 和 t2 都做了一次全表扫描,因此总的扫描行数是 1100。由于 join_buffer 是以无序数组的方式组织的,因此对表 t2 中的每一行,都要做 100 次判断,总共需要在内存中做的判断次数是:100*1000=10 万次。前面我们说过,如果使用 Simple Nested-Loop Join 算法进行查询,扫描行数也是 10 万行。因此,从时间复杂度上来说,这两个算法是一样的。但是,Block Nested-Loop Join 算法的这 10 万次判断是内存操作,速度上会快很多,性能也更好。
4.Multi-Range Read 优化
Multi-Range Read 优化 (MRR)。这个优化的主要目的是尽量使用顺序读盘。字段a存在索引
select * from t1 where a>=1 and a<=100;
主键索引是一棵 B+ 树,在这棵树上,每次只能根据一个主键 id 查到一行数据。因此,回表肯定是一行行搜索主键索引的
MRR 优化的设计思路。此时,语句的执行流程变成了这样:
a.根据索引 a,定位到满足条件的记录,将 id 值放入 read_rnd_buffer 中 ;
b.将 read_rnd_buffer 中的 id 进行递增排序;
c.排序后的 id 数组,依次到主键 id 索引中查记录,并作为结果返回