索引失效的问题如何排查 ?
一、基础概念
MySQL的索引失效是一个比较常见的问题,这种情况一般会在慢SQL发生时需要考虑,考虑是否存在索引失效的问题
在排查索引失效的时候,第一步一定是找到要分析的SQL语句,然后通过 explain 查看他的执行计划。主要关注type(访问类型)、key(实际使用的索引) 和 extra(附加信息) 这几个字段
我们需要通过key+type+extra来判断一条SQL语句是否用到了索引。 如果有用到索引,那么是走了覆盖索引呢?还是索引下推呢?还是扫描了整颗索引树呢?或者是用到了索引跳跃扫描等等
一般来说,比较理想的走索引的话,应该是以下几种情况:
首先,key一定要有值,不能是 NULL
其次,type应该是 ref、eq_ref、range、const等这几个
还有,extra的话,如果是 NULL,或者 using index,using index condition都是可以的
如果通过执行计划之后,发现一条SQL没有走索引,比如 type = ALL, key = NULL , extra = Using where
那么就要进一步分析没有走索引的原因了。我们需要知道的是,到底要不要走索引,走哪个索引,是MySQL的优化器决定的,他会根据预估的成本来做一个决定
那么,有以下这么几种情况可能会导致没走索引:
1、没有正确创建索引:当查询语句中的where条件中的字段,没有创建索引,或者不符合最左前缀匹配的话,就是没有正确的创建索引
2、索引区分度不高:如果索引的区分度不够高,那么可能会不走索引,因为这种情况下走索引的效率并不高
3、表太小:当表中的数据很小,优化器认为扫全表的成本也不高的时候,也可能不走索引
4、查询语句中,索引字段因为用到了函数、类型不一致等导致了索引失效
这时候我们就需要从头开始逐一分析:
1、如果没有正确创建索引,那么就根据SQL语句,创建合适的索引。如果没有遵守最左前缀那么就调整一下索引或者修改SQL语句
2、索引区分度不高的话,那么就考虑换一个索引字段
3、表太小这种情况确实也没啥优化的必要了,用不用索引可能影响不大的
4、排查具体的失效原因,然后针对性的调整SQL语句就行了
二、可能导致索引失效的情况
假设我们有一张表(以下SQL实验基于Mysql 5.7):
  CREATE TABLE `mytable` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(50) NOT NULL,
    `age` int(11) DEFAULT NULL,
    `create_time` datetime DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `name` (`name`),
    KEY `age` (`age`),
    KEY `create_time` (`create_time`)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  insert into mytable(id,name,age,create_time) values (1,"hollis",20,now());
  insert into mytable(id,name,age,create_time) values (2,"hollis1",21,now());
  insert into mytable(id,name,age,create_time) values (3,"hollis2",22,now());
  insert into mytable(id,name,age,create_time) values (4,"hollis3",20,now());
  insert into mytable(id,name,age,create_time) values (5,"hollis4",14,now());
  insert into mytable(id,name,age,create_time) values (6,"hollis5",43,now());
  insert into mytable(id,name,age,create_time) values (7,"hollis6",32,now());
  insert into mytable(id,name,age,create_time) values (8,"hollis7",12,now());
  insert into mytable(id,name,age,create_time) values (9,"hollis8",1,now());
  insert into mytable(id,name,age,create_time) values (10,"hollis9",43,now());
1、索引列参与计算

以上SQL是可以走索引的,但是如果我们在字段中增加计算的话,就会索引失效:

但是如果是如下形式的计算还是可以走索引的:

2、对索引列进行函数操作

以上SQL是可以走索引的,但是如果我们在字段中增加函数操作的话,就会索引失效:

3、使用OR

但是如果使用 OR 的话,并且 OR 的两边存在 < 或者 > 的时候,就会索引失效,如:

但是如果 O R两边都是 = 判断,并且两个字段都有索引,那么也是可以走索引的,如:

4、like操作



以上四种like,"Hollis%" 和 "Holl%is" 这两种可以走索引,但是如果是 "%Hollis%" 和 "%Hollis" 就没办法走索引了
5、隐式类型转换

以上情况,name是一个 varchar 类型,但是我们用 int 类型查询,这种是会导致索引失效的
这种情况有一个特例,如果字段类型为 int 类型,而查询条件添加了 单引号 或 双引号,则 Mysql 会参数转化为 int类型,这种情况也能走索引:

6、不等于比较

但是也不全是,比如以下的用id进行 != 比较的时候,是可能走索引的:

!= 这种情况,索引失效与索引的选择性、数据分布情况等因素有关,不能简单地说是由于查询条件中包含 != 这个操作符而导致索引失效
7、is not null

8、order by

当进行 order by 的时候,如果数据量很小,数据库可能会直接在内存中进行排序,而不使用索引
8、in
使用 in 的时候,有可能走索引,也有可能不走,一般在 in 中的值比较少的时候可能会走索引优化,但是如果选项比较多的时候,可能会不走索引:


 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号