欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 艺术 > 【MySQL】索引失效问题详解

【MySQL】索引失效问题详解

2025/4/23 17:31:46 来源:https://blog.csdn.net/m0_74289770/article/details/147426717  浏览:    关键词:【MySQL】索引失效问题详解

目录

1. 最左前缀原则

2. 条件左边有函数或运算

3. 隐式类型转换

4. LIKE 模糊查询以 % 开头

5、MySQL 优化器选择全表扫描

⭐对 in 关键字特别说明⭐

(1)列表太大时,走全表扫描了

(2)隐式类型转换

(3)最左前缀原则

(4)NOT IN 导致索引失效的场景


        大家好,今天给大家总结一下索引失效的场景。有忽略的欢迎补充!

1. 最左前缀原则

如果索引有多个列,即联合索引,注意他们是有顺序的。指的是查询从索引的最做前列开始匹配,并且不调过索引中的列,一旦跳过了某个索引中的列,那么对这个索引后面的列进行查询就会失效。

(1)加入给name、status、address加了联合索引: 

(2)范围查询(如 >, <, BETWEEN)之后的列不会使用索引: 

2. 条件左边有函数或运算

原因:MySQL 无法利用索引中的值进行范围匹配。

-- 索引失效,因为对字段进行了函数处理
SELECT * FROM user WHERE YEAR(birthday) = 2020;-- 例2:name 字段的第三到第四个字符(即从 name 字段的第3个字符开始,取2个字符)等于 '科技'
SELECT * FROM user WHERE substring(name,3,2) = '科技';

 运算场景:

-- 也会失效:字段参与了运算
SELECT * FROM user WHERE age + 1 = 30;

3. 隐式类型转换

原因:由于在查询的时候,mysql的查询优化器,会自动的进行类型转换,造成索引失效

-- 如果 age 是 int 类型,'18' 是字符串,会触发类型转换
SELECT * FROM user WHERE age = '18';-- 字符串没有加引号,也会进行隐式类型转换;下面这条sql如果status是varchar类型则索引会失效
select * from tb_user where name = '小明' and status = 0

4. LIKE 模糊查询以 % 开头

-- 索引失效(以 % 开头)
SELECT * FROM user WHERE name LIKE '%abc';SELECT * FROM user WHERE name LIKE '%a%';-- 可以使用索引(不以 % 开头)
SELECT * FROM user WHERE name LIKE 'abc%';

5、MySQL 优化器选择全表扫描

即使索引存在,MySQL 优化器也可能选择全表扫描,常出现在以下情况:

  • 表数据太少,扫描成本比走索引还低;
  • 索引选择性太差(重复值多);

示例:超大数据量分页

-- 查询十万页后的数据:
select * from t5 order by text limit 100000, 10;

采用这种SQL查询分页的话,从几百万数据中取出这10行数据的代价是非常大的,需要先排序查出前1000010条记录,然后抛弃前面1000000条。

使用explain分析执行情况来看,在大分页的情况下,MySQL没有走索引扫描,即使text字段已经加上了索引。

主要原因:MySQL在查询代价估算中,认为全表扫描方式比走索引扫描的方式效率更高的话,就会放弃索引,直接全表扫描。

解决:使用覆盖索引或者子查询

-- 使用覆盖索引,不需要回表查询
select id,text from t5 order by text limit 100000, 10;--使用子查询,不需要回表查询
select * from t5 where id>=(select id from t5 order by text limit 100000,1) limit 10

⭐对 in 关键字特别说明⭐

首先,IN 关键字在 MySQL 中 并不会必然导致索引失效,关键在于使用方式是否合理

因为in关键字也会遵循上面所说的最左匹配原则、隐式类型转换、全表扫描的规定。

(1)列表太大时,走全表扫描了

-- 优化器判断走索引不划算,放弃索引
SELECT * FROM user WHERE id IN (1, 2, ..., 10000);-- 子查询返回结果量过大时,索引可能失效
SELECT * FROM user WHERE id IN (SELECT user_id FROM orders);

(2)隐式类型转换

-- id 是 int 类型,但 IN 列表里是字符串
SELECT * FROM user WHERE id IN ('1', '2', '3');

(3)最左前缀原则

-- 有索引 (a, b),以下查询只对 a 有索引效果;因为 b 不是最左前缀,索引不会起效。
SELECT * FROM table WHERE b IN (1, 2, 3);

(4)NOT IN 导致索引失效的场景

SELECT * FROM user WHERE id NOT IN (SELECT user_id FROM orders);
  • 如果 orders.user_id 里有 NULL,那么整个 NOT IN 条件都会为 FALSE 或 UNKNOWN,结果是 整张表都不符合条件索引失效 + 结果错误
  • MySQL 在处理 NOT IN 时,会对每一行都判断是否在列表中,一旦有 NULL,逻辑就崩了。

 解决方法:确保子查询列无 NULL,或使用 NOT EXISTS 替代。 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词