笔记: mysql 索引

81 阅读7分钟

索引下推

mysql> select * from tuser where name like '张%' and age=10 and ismale=1;
(name, age)为联合索引

1、server层第一次开始执行查询,把查询条件交给存储引擎,让存储引擎定位符合条件的
第一条记录。

2、根据前缀索引规则(只能使用到name索引),存储引擎根据条件name like '张%'通过二级索引定位到
满足条件的第一条数据

3、MySQL 5.6之前:然后拿着该二级索引记录中的主键值去回表,把完整的用户记录都取到之后返回
给server层(得到一条二级索引记录后立即去回表,而不是把所有的二级索引记录都拿到后统一去表),
MySQL 5.6之后:引入索引下推的概念,在遍历索引的过程中,对索引中包含的字段先做判断,直接过滤掉
不满足条件的记录,减少回表次数(判断name是否满足'张%'、age是否等于10,如果不满足的话,就跳过
该条记录)

4、接着根据索引继续向后遍历,根据索引可以快速定位到下一条记录的位置,然后继续判断ICP(索引下
推)条件,进行回表操作,存储引擎把下一条记录取出后就将其返回给server层。

5、直到存储引擎层遇到了不符合name like '张%'的记录,然后向server层返回了读取完毕的信息,这
时server层将结束查询。

7、server层将返回的所有数据进行where过滤(判断ismale字段是否成立)

注意:
如果(name, age)索引拆分为name、age单个的二级索引执行情况是怎样的呢?
### 从MySQL5.0之后引入索引合并(index_merge),
1、从idx_name二级索引对应的B+树中取出name like '张%' 的所有记录(会触发ICP) 为啥还要再判
断一次?这就是设计MySQL 的粗暴设计,没有为啥[下面有解释]~

2、再从idx_age二级索引对应的B+树中取出age=10的所有记录。

3、因为二级索引的叶子节点都是由索引列和主键ID构成的,可以计算出两个二级索引结果集中主键ID的交集

4、根据交集中的主键ID进行回表操作

如果发生了同时搜索两棵索引树的事情,大概是你的索引设计有问题,此时就要去检查一下索引的设计
是否合理,这种事情最好能够避免。

索引下推的实现原理

创建一张测试表
create table t1 (a int primary key, b int, c int, d int, e varchar(20)); 
//创建索引
create index idx_t1_bcd on t1(b, c, d); 
insert into t1 values (4,3,1,1,’d’); 
insert into t1 values (1,1,1,1,’a’); 
insert into t1 values (8,8,8,8,’h’);
insert into t1 values (2,2,2,2,’b’);
insert into t1 values (5,2,3,5,’e’);
insert into t1 values (3,3,2,2,’c’);
insert into t1 values (7,4,5,5,’g’); 
insert into t1 values (6,6,4,4,’f’);


这条sql的几个关键性问题:
select * from t1 where b >= 2 and b < 8 and c > 1 and d != 4 and e != 'a';

1.##### 根据覆盖索引idx_t1_bcd确定范围
起始范围:记录[2,2,2]是第一个需要检查的索引项。索引起始查找范围由b >= 2,c > 1决定。
终止范围:记录[8,8,8]是第一个不需要检查的记录,索引的终止查找范围由b < 8决定;

##### 2. 在确定了查询的起始、终止范围之后,SQL中还有哪些条件可以使用索引idx_t1_bcd过滤?
固定了索引的查询范围(2,2,2),(8,8,8)之后,此索引范围中并不是每条记录都是满足where查询条件
的。例如:(3,1,1)不满足c > 1的约束;(6,4,4)不满足d != 4的约束。而c,d列,均可在索引
idx_t1_bcd中过滤掉不满足条件的索引记录的。
因此,SQL中还可以使用c > 1 and d != 4条件进行索引记录的过滤。

##### 3. 在确定了索引中最终能够过滤掉的条件之后,还有哪些条件是索引无法过滤的?
显而易见,e !='a'这个查询条件,无法在索引idx_t1_bcd上进行过滤,因为索引并未包含e列。e列只
在堆表上存在,为了过滤此查询条件,必须将已经满足索引查询条件的记录回表,取出表中的e列,然后
使用e列的查询条件e != ‘a’进行最终的过滤。

在理解以上的问题解答的基础上,做一个抽象,可总结出一套放置于所有SQL语句而皆准的where查询条
件的提取规则,可归纳为3大类:Index Key (First Key & Last Key),Index FilterTable 
Filter

Index Key

用于确定SQL查询在索引中的连续范围的查询条件,被称之为Index Key。一个范围包含一个起始与一个
终止,因此Index Key也被拆分为Index First Key和Index Last Key,分别用于定位索引查找的起
始,以及索引查询的终止条件。

Index First Key:
提取规则:从索引的第一个键值开始,检查其在where条件中是否存在,若存在并且条件是=>=,则将
对应的条件加入Index First Key之中,继续读取索引的下一个键值,使用同样的提取规则;若存在并
且条件是>,则将对应的条件加入Index First Key中,然后终止Index First Key的提取。

Index Last Key:
与Index First Key正好相反,用于确定索引查询的终止范围。
提取规则:从索引的第一个键值开始,检查其在where条件中是否存在,若存在并且条件是=<=,则将
对应条件加入到Index Last Key中,继续提取索引的下一个键值,使用同样的提取规则;若存在并且
条件是 < ,则将条件加入到Index Last Key中,同时终止提取;若不存在,同样终止Index Last 
Key的提取。

Index Filter

在Index Key的提取之后固定了索引的查询范围,但是此范围中的项,并不都是满足查询条件的项。在
上面的SQL用例中,(3,1,1),(6,4,4)均属于范围中,但是均不满足SQL的查询条件。

Index Filter的提取规则:
同样从索引列的第一列开始,检查其在where条件中是否存在
1. 若存在并且where条件仅为 =,则跳过第一列继续检查索引下一列,下一索引列采取与索引第一列同
样的提取规则;
2.where条件为 >=><<= 其中的几种,则跳过索引第一列,将其余where条件中索引相关列全
部加入到Index Filter之中;
3. 若索引第一列的where条件包含 =>=><<= 之外的条件,则将此条件以及其余where条件中
索引相关列全部加入到Index Filter之中;

这里其实就是对上面例子中的这句话的具体实现:
> MySQL 5.6之后:引入索引下推的概念,在遍历索引的过程中,对索引中包含的字段先做判断,直接过滤掉
> 不满足条件的记录,减少回表次数(判断name是否满足'张%'、age是否等于10,如果不满足的话,就跳过
> 该条记录)

Table Filter

Table Filter是最简单,也是提取最为方便的。
提取规则:所有不属于索引列的查询条件,均归为Table Filter之中。
针对上面的用例SQLTable Filter就为 e != 'a'

总结

SQL语句中的where条件,使用以上的提取规则,最终都会被提取到Index Key (First Key & Last 
Key),Index FilterTable Filter之中。

Index First Key,只是用来定位索引的起始范围,在索引第一次Search Path(沿着索引B+树的根节
点一直遍历,到索引正确的叶节点位置)时使用,一次判断即可;

Index Last Key,用来定位索引的终止范围,因此对于起始范围之后读到的每一条索引记录,均需要
判断是否已经超过了Index Last Key的范围,若超过,则当前查询结束;

Index Filter,用于过滤索引查询范围中不满足查询条件的记录,因此对于索引范围中的每一条记录,
均需要与Index Filter进行对比,若不满足Index Filter则直接丢弃,继续读取索引下一条记录;

Table Filter,最后一道where条件的防线,用于过滤通过前面索引的层层考验的记录,判断完整记录
是否满足Table Filter中的查询条件,若不满足,跳过当前记录,继续读取索引的下一条记录,若满
足,则返回记录,此记录满足了where的所有条件。

Index Key (First Key & Last Key),Index FilterTable Filter其实就是 Index Condition
Pushdown(ICP, 索引条件下推) 的具体实现

如果某个查询语句使用了ICP特性,Extra会出现补充信息Using index condition 
注意:
对于使用二级索引进行等值查询的情况有些许不同,对于只有等值查询的这种情况,InnoDB存储引擎层
有特殊的处理方案,是不作为ICP条件进行处理的