Appearance
mysql索引失效的几种场景
1.准备工作
1.1创建表
sql
DROP TABLE IF EXISTS `movie`;
CREATE TABLE `movie` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`code` varchar(50) DEFAULT NULL COMMENT '电影编号',
`type` int(11) DEFAULT NULL COMMENT '电影类型',
`name` varchar(200) DEFAULT NULL COMMENT '电影名称',
`ranks` int(11) DEFAULT NULL COMMENT '排名',
`remark` varchar(200) DEFAULT NULL COMMENT '描述',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;1.2创建索引
sql
ALTER TABLE movie ADD index idx_code_type_name (code, type, name);
ALTER TABLE movie ADD index idx_ranks (ranks);1.3插入数据
sql
INSERT INTO `movie` (`code`, `type`, `name`, `ranks`, `remark`) VALUES ('101', 3, '战狼', 1, '战狼:备注');
INSERT INTO `movie` (`code`, `type`, `name`, `ranks`, `remark`) VALUES ('202', 2, '流浪地球', 3, '流浪地球:备注');
INSERT INTO `movie` (`code`, `type`, `name`, `ranks`, `remark`) VALUES ('303', 1, '独行月球', 5, '独行月球:备注');2.索引失效场景
2.1 不满足最左匹配原则
sql
EXPLAIN
SELECT * from movie where code = '101' and type = 1 and name = '战狼'; #走索引
EXPLAIN
SELECT * from movie where code = '101' and type = 1; #走索引
EXPLAIN
SELECT * from movie where code = '101' ; #走索引
EXPLAIN
SELECT * from movie where code = '101' and name = '战狼'; #走索引,只是用上了code字段索引
EXPLAIN
SELECT * from movie where name = '战狼' and code = '101'; #走索引
EXPLAIN
SELECT * from movie where type = 1 and name = '战狼'; #不走索引因为联合索引的情况下,数据是按照索引第一列排序,在第一列数据相同才会按照第二列排序,第三列又是根据前面两列排序后再来排序
中间断层这种,其实他只是走了最左边的字段索引,剩下的没用上
查询条件中,只要有最左边的索引字段,就会走索引;和where条件字段顺序无关,即使中间断了层也能走上索引
2.2 使用了 select *
sql
EXPLAIN
select * from movie ;#不走索引sql中使用了select *,从执行结果来看,走了全表扫描,没有用到任何索引,查询效率非常低
sql
EXPLAIN
SELECt code, type, name from movie;#走索引如果select语句中的查询列都是索引列,那么这些列被称为覆盖索引。这种情况下,查询的相关字段都能走索引,索引查询的效率相对高一些
2.3 order by导致索引失效
sql
EXPLAIN
SELECT code,type,name,ranks from movie ORDER BY ranks;#不走索引
EXPLAIN
SELECT ranks from movie ORDER BY ranks;#走索引在我们的开发过程中,经常会使用到order by,为了保证order by的查询效率,建议将order by字段和select字段添加联合索引;但是如果需求经常变更需要增加字段,不能总是去修改select字段的联合索引;可以通过给order by增加索引,先通过子查询查询出id,然后再手动回表查询所需要的字段
2.4 索引列上有计算/使用了函数
sql
EXPLAIN
SELECT * from movie where IFNULL(ranks,0)=0;#不走索引
EXPLAIN
SELECT * from movie where ranks+1=2;#不走索引会对索引列进行一次重新计算
2.5 字段类型不同
sql
EXPLAIN
SELECT * from movie where code='101';#走索引
EXPLAIN
SELECT * from movie where code=101;#不走索引
EXPLAIN
SELECT * from movie where rank='101';#走索引mysql如果是int类型字段作为查询条件时,他会自动将该字段的参数进行隐式转换,把字符串换成int类型
2.6 like左边包含了%
sql
EXPLAIN
SELECT * from movie where code like '%1';#不走索引
EXPLAIN
SELECT * from movie where code like '1%';#走索引
EXPLAIN
SELECT * from movie where code like '%1%';#不走索引
EXPLAIN
SELECT code, name, type from movie where code LIKE '%1%';#走索引当like语句中的%出现在查询条件的左边时,索引会失效;但是如果我们使用覆盖索引就不会失效
2.7 列对比
sql
EXPLAIN
SELECT * from movie where id=ranks;#不走索引
EXPLAIN
SELECT ranks from movie where id=ranks;#走索引
EXPLAIN
SELECT code, type, name from movie where id=type;#走索引如果把两个单独建了索引的列用来做对比时索引会失效
走覆盖索引的话是可以走索引的,比如只查询ranks字段或者将where条件字段换成我们的聚合索引上面的字段
2.8 使用or关键字
sql
EXPLAIN
SELECT * from movie where id=1 or ranks=1;# or字段前后两个字段都增加了索引,mysql8会走索引,mysql5.7不会走索引
EXPLAIN
SELECT * from movie where id=1 or remark='战狼:备注';#remark字段没有索引,增加or后会不走索引如果使用了or关键字,那么他前面和后面的字段都要加索引,不然所有的索引都会失效。这里是一个大坑
2.9 <>不等于导致索引失效
sql
EXPLAIN
SELECT * from movie where ranks=1;#走索引
EXPLAIN
SELECT * from movie where ranks<>1;#不走索引mysql5.7版本<>不等于会导致索引失效;但是在mysql8.0还是会走索引
不走索引场景解析:其实它是可以走索引的,如果返回结果集大于20%,那么它就不会走索引。
2.10. 范围查询数量过多导致索引失效
sql
INSERT INTO `movie` (`code`, `type`, `name`, `ranks`, `remark`) VALUES ('104', 4, '流浪地球1', 2, '流浪地球1:备注');
INSERT INTO `movie` (`code`, `type`, `name`, `ranks`, `remark`) VALUES ('105', 5, '独行月球2', 4, '独行月球2:备注');
INSERT INTO `movie` (`code`, `type`, `name`, `ranks`, `remark`) VALUES ('106', 6, '战狼3', 6, '战狼3:备注');
INSERT INTO `movie` (`code`, `type`, `name`, `ranks`, `remark`) VALUES ('107', 7, '流浪地球4', 7, '流浪地球4:备注');
INSERT INTO `movie` (`code`, `type`, `name`, `ranks`, `remark`) VALUES ('108', 8, '独行月球5', 8, '独行月球5:备注');
INSERT INTO `movie` (`code`, `type`, `name`, `ranks`, `remark`) VALUES ('109', 9, '战狼6', 9, '战狼6:备注');
INSERT INTO `movie` (`code`, `type`, `name`, `ranks`, `remark`) VALUES ('110', 10, '流浪地球7', 10, '流浪地球7:备注');
INSERT INTO `movie` (`code`, `type`, `name`, `ranks`, `remark`) VALUES ('111', 11, '独行月球8', 11, '独行月球8:备注');
INSERT INTO `movie` (`code`, `type`, `name`, `ranks`, `remark`) VALUES ('112', 12, '战狼9', 12, '战狼9:备注');sql
EXPLAIN
SELECT * from movie WHERE ranks>1;#不走索引
EXPLAIN
SELECT * from movie WHERE ranks>10;走索引where条件大于1的情况下,因为扫描的行数比较多,还不如全表扫描;但是当ranks大于10的时候,数据比较少,回表成本也比较小,所以还是会选择走索引;
在我们开发过程中如果遇到范围条件,尽可能去精确一点
3. 总结
我们在实际开发过程中 ,一定要注意以上这些场景,我们要灵活运用我们的索引;尽量使用覆盖索引,减少select * 的这种写法,更要关心我们的查询条件的数据量
更新: 2023-08-07 16:56:02
原文: https://www.yuque.com/tulingzhouyu/sfx8p0/gxbfeagi2g49gipa