图文并茂,深刻了解索引失效

不满足最左前缀法则

如果索引了多列(联合索引才有最左前缀法则),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过(这个跳过其实就是缺少,跟放的位置没有关系,放在哪里都可以,只要存在这个条件就好了)索引中的列。如果跳跃某一列,索引将会部分失效(后面的字段索引失效)。

以 tb_user 表为例,我们先来查看一下之前 tb_user 表所创建的索引。

img

在 tb_user 表中,有一个联合索引,这个联合索引涉及到三个字段,顺序分别为:profession, age,status。

对于最左前缀法则指的是,查询时,最左边的列,也就是profession必须存在,否则索引全部失效。 而且中间不能跳过某一列(缺少查询某个字段),否则该列后面的字段索引将失效。 接下来,我们来演示几组案例,看一下 具体的执行计划:

explain select * from tb_user where profession = '软件工程' and age = 31 and status = '0';

img

explain select * from tb_user where profession = '软件工程' and age = 31;  

img

explain select * from tb_user where profession = '软件工程';  

img

以上的这三组测试中,我们发现只要联合索引最左边的字段 profession存在,索引就会生效,只不过索引的长度不同。

而且由以上三组测试,我们也可以推测出profession字段索引长度为47、age 字段索引长度为2、status字段索引长度为5。

下面来演示索引失效的情况:

explain select * from tb_user where age = 31 and status = '0';

img

explain select * from tb_user where status = '0';  

img而通过上面的这两组测试,我们也可以看到索引并未生效,原因是因为不满足最左前缀法则,联合索引最左边的列profession不存在。

explain select * from tb_user where profession = '软件工程' and status = '0';  

img

上述的SQL查询时,存在profession字段,最左边的列是存在的,索引满足最左前缀法则的基本条件。但是查询时,跳过了age这个列,所以后面的列索引是不会使用的,也就是索引部分生效,所以索引的长度就是47。

explain select * from tb_user where age = 31 and status = '0' and profession = '软件工程';

img

可以看到,是完全满足最左前缀法则的,索引长度54,联合索引是生效的。

注意 : 最左前缀法则中指的最左边的列,是指在查询时,联合索引的最左边的字段(即是 第一个字段)必须存在,与我们编写SQL时,条件编写的先后顺序无关。

出现范围查询(>,<)

联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。(注意是右侧是下一个条件,进行范围查询的这个条件还是会走索引的)

explain select * from tb_user where profession = '软件工程' and age > 30 **and status = '0'****;**

img

索引的长度为49,就说明范围查询右边的status字段是没有走索引的。

explain select * from tb_user where profession = '软件工程' and age >= 30 and status = '0';  

img

当范围查询使用>= 或 <= 时,走联合索引了,但是索引的长度为54,就说明所有的字段都是走索引的。 所以,在业务允许的情况下,**尽可能的使用类似于 >= 或 <= 这类的范围查询**,而避免使用 > 或 < 。

索引列运算

不要在索引列上进行运算操作(包括内置函数), 索引将失效。

在tb_user表中,除了前面介绍的联合索引之外,还有一个索引,是phone字段的单列索引。

A. 当根据phone字段进行等值匹配查询时, 索引生效。

explain select * from tb_user where phone = '17799990015';

img

B. 当根据phone字段进行函数运算操作之后,索引失效。

explain select * from tb_user where substring(phone,10,2) = '15';

img

字符串不加引号

字符串类型字段使用时,不加引号其实是可以查询出来的,跟普通的加引号是一样的,但是如果不加引号,索引将失效。

接下来,我们通过两组示例,来看看对于字符串类型的字段,加单引号与不加单引号的区别:

explain select * from tb_user where profession = '软件工程' and age = 31 and status= '0';--走索引
explain select * from tb_user where profession = '软件工程' and age = 31 and status= 0;--不走索引

img经过上面两组示例,我们会明显的发现,如果字符串不加单引号,对于查询结果,没什么影响,但是数 据库存在隐式类型转换,索引将失效。

模糊查询

如果仅仅是尾部模糊匹配(也就是”胥%”),索引不会失效。如果是头部模糊匹配(也就是”%天昊”),索引失效。

接下来,我们来看一下这三条SQL语句的执行效果,查看一下其执行计划:

由于下面查询语句中,都是根据profession字段查询,符合最左前缀法则,联合索引是可以生效的,我们主要看一下,模糊查询时,%加在关键字之前,和加在关键字之后的影响。

explain select * from tb_user where profession like '软件%';--走索引
explain select * from tb_user where profession like '%工程';--索引失效
explain select * from tb_user where profession like '%工%';--索引失效

img经过上述的测试,我们发现,在like模糊查询中,在关键字后面加%,索引可以生效。而如果在关键字前面加了%,索引将会失效。

or连接条件两边必须都得有索引

用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到,也就是说:只有两个都有索引才会走索引,一个没有都不行。

下面的例子中id字段和phone字段有索引,age字段没有索引,

explain select * from tb_user where id = 10 or age = 23;--age没索引,所以索引失效
explain select * from tb_user where phone = '17799990017' or age = 23;--age没索引,所以索引失效

img

所以: 当or连接的条件,左右两侧字段都有索引时,索引才会生效。

数据分布影响走不走索引

如果MySQL评估使用索引比全表更慢,则不使用索引。

select * from tb_user where phone >= '17799990005';--大部分都比这个大,所以索引失效
select * from tb_user where phone >= '17799990015';--只有一小部分比这个大,所以索引可以实现

img

因为MySQL在查询时,会评估(是mysql帮我们做的工作,是自动的)使用索引的效率与走全表扫描的效率,如果走全表扫描更快,则放弃索引,走全表扫描。 因为索引是用来索引少量数据的,如果通过索引查询返回大批量的数据,则还不如走全表扫描来的快,此时索引就会失效。