Skip Index Scan(跳跃式索引)
例如初中有个学生表,年级、班级、学号 符合索引。
-- 问题是下面这个查询为什么也可以用到索引。
select * from 初中学生表 where 班级 = 1 and 学号 = 001
-- 思考一下这个查询比全表扫描快吗?
select * from 初中学生表 where (年级=1 and 班级 = 1 and 学号 = 001)
or (年级=2 and 班级 = 1 and 学号 = 001)
or (年级=3 and 班级 = 1 and 学号 = 001)
Skip Index Scan的基本原理
考虑一个例子,如果表t1有索引(c1, c2),但查询的条件中只包含c2,不含c1,正常情况下,我们是无法使用这个索引的。即使使用这个索引,也必须做index的全表扫描,然后在rows上做condition操作,如select c1, c2 from t1 where c2 > 10;
但实际上,这里还是有优化的空间的,能充分利用索引的优势,尽可能的减少扫描的rows,这就是引入skip scan的原因,这个方法是由facebook贡献给mysql的,在8.0.13之后开始支持。
Skip index scan的原理很简单:通过skip 索引的前缀,对后续索引部分做小范围的range scan。
案例如下:
一张表如下
id | sex | age |
---|---|---|
xx1 | F | 22 |
xx2 | M | 21 |
xx3 | M | 23 |
xx4 | M | 31 |
建立了联合索引为index(sex,age)
当查询sql为 ‘SELECT * FROM X WHERE age > 30’ 时,通过skip index scan也会走联合索引进行查找,原因如下
- 对于联合索引来说,你想查age大于30的值,不就是当sex等于F或者M的时候age大于30的行吗
- 所以mysql会对查询进行优化,先通过联合索引的最左列sex的值配合B+树来在不同的sex值之间跳跃,找到每个sex中age大于30的行,比如你的sex不就时F和M吗,通过联合索引的B+树找到F的范围所在,再找age>30的数据,找完后直接跳到M所在范围找age>30的行
- 所以最后优化为了 ‘SELECT * FROM X WHERE (sex = ‘F’ and age > 30) or (sex = ‘M’ and age > 30)’
- 所谓的跳跃式索引扫描就是在对于没有最左原则匹配的情况下的查询,通过为最左列的值进行跳跃式的匹配,来达到原本的效果,从而避免以前的没有最左匹配就走全表索引
Skip Index Scan的常见应用场景
如表t5有索引c1, c2,其中distinct(c1)值很小。若在查询select c1, c2 from t5 where c2 > 30; 如下图所示:
如图所示,对于c1只有2个值,F/M,那么就可以将条件扩展为 (c1=‘F’ and c2 > 30) OR (c1=‘M’ and c2 > 30)
Skip Index Scan的限制
需要注意的是,Skip Index Scan有一些使用限制:
https://www.itworkman.com/index-skip-scan-of-mysql-8-0/
- select中选择的字段不能包含非索引字段。
- SQL语句不能包含group by或distinct语法。
- Skip Index Scan只支持单表查询,不能用于多表联接。