索引失效的案例
导语
建立索引可以提高查询数据的速度,但有时候建完索引并没有达到预期的效果,索引没有被使用,仍然走了全表扫描。下文将通过一些案例分析索引失效的情况。
前置知识
B+Tree 索引结构
B+Tree中,非叶子节点存放索引,叶子节点存放数据。每个节点按页存储,叶子节点之间用双链表连接。示意图如下:
聚集索引 VS 二级索引
聚集索引也叫一级索引、聚簇索引、主键索引,是对表中的主键创建的索引,叶子节点存放的是行数据。
对表中的非主键字段创建索引,创建出来的是二级索引,二级索引的叶子节点存放主键值,如果从二级索引中查不到需要的信息,就会拿着主键值去聚集索引里查,这个过程叫回表查询。
回表查询
例如现在有一张表:student(id, stuno, name, age, height, score),主键是 id,对name字段建立索引,执行如下语句会出现回表查询:
sql
select * from student where name = 'Jhon';
回表查询的过程:从二级索引取到 id 值,再去聚集索引里查行数据。
索引失效案例分析
以下案例基于MySQL 8,InnoDB 存储引擎,B+Tree索引
1、联合索引未遵循最左匹配原则
表结构:student(id, stuno, name, age, height, score),(age、height、score均为int类型)。对 age、height、score 建立联合索引:
sql
create index idx_ahs on student(age, height, score);
分析如下查询语句:
索引未失效
where age = 15;
sql
explain select * from student where age = 15;
where age = 15 and height = 158;
sql
explain select * from student where age = 15 and height = 158;
where age = 15 and score = 90; 跳过了中间的 height 字段,索引下推
sql
explain select * from student where age = 15 and score = 90;
where age = 15 and height = 158 and score = 90;
sql
explain select * from student where age = 15 and height = 158 and score = 90;
调换顺序 依然可以生效:where score = 90 and height = 158 and age = 15;
sql
explain select * from student where score = 90 and height = 158 and age = 15;
范围查询
where age > 15 and height = 158;
where age >= 15 and height = 158;
sql
explain select * from student where age>15 and height=158;
sql
explain select * from student where age>=15 and height=158;
索引失效,未遵循最左匹配
where height = 158;
where height = 158 and score = 90;
sql
explain select * from student where height = 158;
explain select * from student where height = 158 and score = 90;
分析
在联合索引(a,b,c)中,先根据 a 排序,a 相同时再根据 b 排序,b相同时再根据 c 排序。所以对于全体 a 而言是有序的,对于 b 来说,只在 a 相同的时候才有序,从整体上看 b 是无序的,c 类似。
因此,查询时如果跳过 a ,直接根据 b 查,会走全表扫描,因为 b 整体上是无序的。
2、模糊匹配 like '%XXX'
对 student 表的 name 字段建立普通索引:
sql
create index idx_name on student(name);
使用左模糊匹配会失效
where name like '%k';
where name like '%o%';
sql
explain select * from student where name like '%k';
explain select * from student where name like '%o%';
3、对索引使用函数或表达式运算
where length(name) = 4;
sql
explain select * from student where length(name) =4;
where id + 1 = 3;
sql
explain select * from student where id + 1 = 3;
这样索引不会失效:where id = 3 - 1;
sql
explain select * from student where id = 3 - 1;
4、对索引做类型转换
对 stuno 字段创建索引:
sql
create index idx_stuno on student(stuno);
where stuno= 1; stuno是 varchar,查询时使用 int ;索引失效
sql
explain select * from student where stuno = 1;
where age = '15'; 对 int 类型使用 字符串查找,索引不失效
sql
explain select * from student where age = '15';
5、or 连接的多条件查询中有非索引字段
id 和 stuno 都有索引 where id = 1 or stuno = '1'; 索引未失效
sql
explain select * from student where id = 1 or stuno = '1';
将 stuno 的索引删除 ,走全表扫描
id 有索引, stuno 没有索引 where id = 1 or stuno = '1';
sql
drop index idx_stuno on student;
explain select * from student where id = 1 or stuno = '1';
结语
本文分析了一些常见的索引失效的情况,各位小伙伴们在开发过程中还遇到哪些索引失效的情况呢,欢迎在评论区交流讨论。
祝各位创建的索引都能生效!