本文讲解的例子使用的sql:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE TABLE staffs(
  id INT PRIMARY KEY AUTO_INCREMENT,
  NAME VARCHAR(24) NOT NULL DEFAULT '' COMMENT '姓名',
  age INT NOT NULL DEFAULT 0 COMMENT '年龄',
  pos VARCHAR(20) NOT NULL DEFAULT '' COMMENT '职位',
  add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间'
) CHARSET utf8 COMMENT '员工记录表';


INSERT INTO staffs(NAME,age,pos,add_time) VALUES('z3',22,'manager',NOW());
INSERT INTO staffs(NAME,age,pos,add_time) VALUES('July',23,'dev',NOW());
INSERT INTO staffs(NAME,age,pos,add_time) VALUES('2000',23,'dev',NOW());


ALTER TABLE staffs ADD INDEX idx_staffes_nameAgePos(NAME,age,pos);

1、全值匹配我最爱

这句话的意思是我们在写sql的时候尽量使用覆盖索引,即查询的where后面的条件包括使用的字段、个数以及顺序和建立的复合索引一致,如上面的sql我们建立了索引:
图片
索引idx_staffes_nameAgePos按name、age、pos的顺序:
图片
如果不使用*,而select的查询字段也和索引一致的话:
图片
我们看到Extra列出现了Using index,效果最佳,这也是为啥不建议使用select *的原因之一。
同时大家注意下,覆盖索引时explain各属性列的值,尤其是type、key、ref、rows以及Extra列与下面哥案例的变化。

2、最佳左前缀法则

如果引用了多列,要遵守最左前缀法则,指的是查询从索引的最左前列开始并且不跳过索引的列。按idx_staffes_nameAgePos索引来说,查询应该从name属性开始,并且不能越过age属性,我们来看看下面sql例子:

2.1、没有从最前列开始

图片
我们看到type列变成了All全表扫描,而key列也为null,并没有使用到索引,这是索引失效的一个典型的案例,那如果不使用select * 会怎样:
图片
type列变成了index,比select *效果好多了,但远没有覆盖查询效率高。

2.2、跳过中间索引列

我们看下面案例:
图片
where条件下去掉了索引中间的数学age,虽然使用到了索引,但是ref列的const常量只有一个,也就是虽然使用了索引,但只是用到了name属性,仅仅三分之一而已。

3、不在索引列上做任何操作

操作一般指:计算,函数、(自动or手动)类型转换,这样会导致索引失效而转向全表扫描,下面看下使用left函数的例子:
图片
此时我们看到type是all全表扫描,而key列也是null,根本没有用到索引。

4、存储引擎不能使用范围条件右边的列

这句话的意思是,如果索引中的列使用了范围查询,范围一般指在where查询条件中使用in、>(>=)、<(<=)以及between等范围的语句,可能会导致索引部分失效:
图片
结果中虽然key列中显示使用了索引,但是type变成了range,ref列为null,降低了性能。我们在看看不使用select *的情况:
图片
虽然使用了索引,但是ref列只有一个const常量,并没有使用的全部索引。

5、使用不等于(!=或者<>)导致全表扫描

MySQL在使用不等于的时候无法使用索引会导致全表扫描:
图片

6、is null,is not null 也会导致索引失效

图片

7、like的通配符导致全表扫描

like通配符有三种情况:%在前,%在后,%前后都有,下面我们分别看下这三种情况。

7.1、%在前

图片

7.2、%在后

图片

7.3、%前后都有

图片

由上面三种情况可得而知,只有%在后才使用了索引,且type还是range级别。

7.4、解决%号前后都有的情况索引失效的问题

问题:解决like ‘%字符串%’时索引不被使用的方法?
可以使用覆盖索引的办法,不仅是这个问题,以上出现索引失效的问题都可以使用覆盖索引来解决,这也是为啥推荐使用覆盖索引。我们看下结果:

图片
type从all变成了index,且key列显示使用了索引。

8、字符串不加单引号引起索引失效

这个问题也是比较严重的问题,先不说可能会导致失败,加入出现全数字的字符串,就如我们开头插入语句中,第三条数据的name是字符串2000,那么我们先看看是否不加引号是否可以正确查询出数据:
图片
显而易见查询并没有什么问题,这是因为MySQL优化器为我们的sql做了类型的转换。我们再来使用explain分析这条语句:
图片
看看type的类型为all,导致了全表扫描,那么如果name加了双引号呢:
图片
type是ref级别,且使用了索引,所有结论便是,我们尽量不能干这种让MySQL内部做类型转换的事情。

9、使用or连接时导致索引失效

or尽量少用,看看例子:
图片
全表扫描,并未使用的索引。

10、总结

对于索引正确使用的案例,总结下面几句话:

全值匹配我最爱,最左前缀要遵守
带头大哥不能死,中间兄弟不能断
索引列上少计算,范围之后全失效
LIKE百分写最右,覆盖索引不写星
不等空值还有or,索引失效要少用

解析:第一行是对节1、2的说明,而第二行是对最左前缀要遵守的解析,第三行分别是节3、4;第四行是节7的说明,最后一行概括了节5、6、8,9的内容。

最后更新: 2019年07月02日 17:41

原始链接: https://www.sunnymaple.cn/2019/07/02/如何避免索引失效?/

× 请我吃糖~
打赏二维码