索引失效的问题如何排查?
典型回答 MySQL的索引失效是一个比较常见的问题,这种情况一般会在慢SQL发生时需要考虑,考虑是否存在索引失效的问题。 在排查索引失效的时候,第一步一定是找到要分析的SQL语句,然后通过explain查看他的执行计划。主要关注type、key和extra这几个字段。 ✅SQL执行计划分析的时候,要关注哪些信息? 我们需要通过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): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 2 3 4 5 6 7 select * from mytable where age = 12; +----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+ | 1 | SIMPLE | mytable | NULL | ref | age | age | 5 | const | 1 | 100.00 | NULL | +----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+ 以上SQL是可以走索引的,但是如果我们在字段中增加计算的话,就会索引失效: ...