一:索引失效场景

记忆“模型数空运最快”

1.模糊查询:

通配符在开头的LIKE操作:LIKE操作中,如果通配符位于开头(如LIKE '%keyword'),索引将失效,因为数据库无法高效匹配这种模式。

如何让like走索引 like怎么走索引_如何让like走索引

2.类型:列类型不匹配:

若在查询条件中使用与索引列类型不匹配的值,如字符串与数值类型混用,索引可能无法正常工作。

如何让like走索引 like怎么走索引_sql_02

3.函数:使用函数操作索引列:

当我们在查询条件中使用函数,如CONCATSUBSTRINGDATE_FORMAT等,索引将失效。因为这些函数会对索引列进行计算,数据库难以直接匹配预期的值。

如何让like走索引 like怎么走索引_数据库_03

4.空:使用 IS NULL 和 IS NOT NULL

SELECT ... FROM emp WHERE comm IS NULL; comm 列的索引会失效

5.对索引列进行数学运算:

若对索引列进行数学运算,如+-*/等,索引同样会失效。因为数据库无法直接利用索引进行这些运算。

如何让like走索引 like怎么走索引_字段_04

6.最左:使用复合索引,索引的第一字段为空

使用复合索引需符合最左前缀匹配原则

select * from A where name=‘xxx’ and city =‘xxx’  //命中索引
select * from A where name=‘xxx’  //命中索引
select * from A where city =‘xxx’  //无法命中索引

7.快:如果使用全表扫描比索引扫描还快,那就自动放弃索引扫描

8.使用or关键字

某天你遇到这样一个需求:想查一下id=1或者height=175的用户。

你三下五除二就把sql写好了:

explain select * from user 
where id=1 or height='175';

执行结果:

如何让like走索引 like怎么走索引_sql_05

没错,这次确实走了索引,恭喜被你蒙对了,因为刚好id和height字段都建了索引。

但接下来的一个夜黑风高的晚上,需求改了:除了前面的查询条件之后,还想加一个address='成都'。

这还不简单,sql走起:

explain select * from user 
where id=1 or height='175' or address='成都';

执行结果:

如何让like走索引 like怎么走索引_如何让like走索引_06

结果悲剧了,之前的索引都失效了。

你可能一脸懵逼,为什么?我做了什么?

答:因为你最后加的address字段没有加索引,从而导致其他字段的索引都失效了。

注意:如果使用了or关键字,那么它前面和后面的字段都要加索引,不然所有的索引都会失效,这是一个大坑。

9.使用NOT操作:

NOT操作同样会导致索引失效,因为它会对查询条件进行取反操作,使得索引无法直接匹配。

如何让like走索引 like怎么走索引_sql_07

10. not in和not exists

在我们日常工作中用得也比较多的,还有范围查询,常见的有:

  • in
  • exists
  • not in
  • not exists
  • between and

今天重点聊聊前面四种。

10.1 in关键字

假如我们想查出height在某些范围之内的用户,这时sql语句可以这样写:

explain select * from user
where height in (173,174,175,176);

执行结果:

如何让like走索引 like怎么走索引_sql语句_08

从图中可以看出,sql语句中用in关键字是走了索引的。

10.2 exists关键字

有时候使用in关键字时性能不好,这时就能用exists关键字优化sql了,该关键字能达到in关键字相同的效果:

explain select * from user  t1
where  exists (select 1 from user t2 where t2.height=173 and t1.id=t2.id)

执行结果:

如何让like走索引 like怎么走索引_数据库_09

从图中可以看出,用exists关键字同样走了索引。

10.3 not in关键字

上面演示的两个例子是正向的范围,即在某些范围之内。

那么反向的范围,即不在某些范围之内,能走索引不?

话不多说,先看看使用not in的情况:

explain select * from user
where height not in (173,174,175,176);

执行结果:

如何让like走索引 like怎么走索引_sql_10

你没看错,索引失效了。

看如果现在需求改了:想查一下id不等于1、2、3的用户有哪些,这时sql语句可以改成这样:

explain select * from user
where id  not in (173,174,175,176);

执行结果:

如何让like走索引 like怎么走索引_如何让like走索引_11

你可能会惊奇地发现,主键字段中使用not in关键字查询数据范围,仍然可以走索引。而普通索引字段使用了not in关键字查询数据范围,索引会失效。

10.4 not exists关键字

除此之外,如果sql语句中使用not exists时,索引也会失效。具体sql语句如下:

explain select * from user  t1
where  not exists (select 1 from user t2 where t2.height=173 and t1.id=t2.id)

执行结果:

如何让like走索引 like怎么走索引_数据库_12

从图中看出sql语句中使用not exists关键后,t1表走了全表扫描,并没有走索引。

11.使用!=或<>

避免在 where 子句中使用!=或<>操作符

12. 使用了select *

在《阿里巴巴开发手册》中明确说过,查询sql中禁止使用select * 。

那么,你知道为什么吗?

废话不多说,按照国际惯例先上一条sql:

explain 
select * from user where name='苏三';

执行结果:

如何让like走索引 like怎么走索引_如何让like走索引_13

在该sql中用了select *,从执行结果看,走了全表扫描,没有用到任何索引,查询效率是非常低的。

如果查询的时候,只查我们真正需要的列,而不查所有列,结果会怎么样?

非常快速地将上面的sql改成只查了code和name列,太easy了:

explain 
select code,name from user 
where name='苏三';

执行结果:

如何让like走索引 like怎么走索引_字段_14

从图中执行结果不难看出,该sql语句这次走了全索引扫描,比全表扫描效率更高。

其实这里用到了:覆盖索引

如果select语句中的查询列,都是索引列,那么这些列被称为覆盖索引。这种情况下,查询的相关字段都能走索引,索引查询效率相对来说更高一些。

而使用select *查询所有列的数据,大概率会查询非索引列的数据,非索引列不会走索引,查询效率非常低。

二》覆盖索引:索引中like ‘%str' 通配符位于开头且走索引

不失效场景

  • 比如说我有下面这条SQL:select id,name from `user` where name like '%牛'; 首先id,name 这两个字段都在我们的二级索引中,叶子节点存的索引值和主键值,所以我们只要二级索引就可以直接拿到我们的需要的字段,这个叫做索引覆盖。我们观察执行计划会发现它的查询级别是 index ,其实也是全表遍历了二级索引。

那为什么就要走二级索引而不是走全表扫描呢?

  • 因为二级索引中记录的东西比主键索引少了很多,只有索引值和主键值,一页中可以存储更多数据,再加上索引覆盖不用回表,优化器就认为直接遍历辅助索引的效率高于主键索引。