索引失效场景
1. 最左前缀原则失效
假设联合索引:
CREATE INDEX idx_abc ON user(name, age, city);索引顺序:
name → age → city可以走索引
where name='Tom'where name='Tom' and age=20where name='Tom' and age=20 and city='BJ'不能走索引
where age=20where city='BJ'where age=20 and city='BJ'因为缺少最左列:
nameB+Tree 无法定位起始位置。
2. 索引列使用函数
例如:
where YEAR(create_time)=2025索引:
create index idx_time on user(create_time);失效原因:
数据库需要先计算:
YEAR(create_time)B+Tree 存的是:
2025-06-16 10:00:00不是:
2025无法直接利用索引。
优化:
where create_time >= '2025-01-01' and create_time < '2026-01-01'3. 索引列参与运算
例如:
where age + 1 = 20索引:
idx_age(age)失效。
因为:
数据库需要先算 age+1无法直接使用 B+Tree。
改为:
where age = 194. 隐式类型转换
表:
phone varchar(20)索引:
idx_phone(phone)查询:
where phone = 13800138000注意:
phone 是 varchar 条件是数字MySQL 可能会:
CAST(phone AS SIGNED)变成:
函数作用于索引列导致索引失效。
正确写法:
where phone='13800138000'5. LIKE 左模糊查询
索引:
idx_name(name)可以走索引
where name like 'Tom%'因为:
Tom Tom1 Tom2 Tom3范围明确。
不能走索引
where name like '%Tom'或者:
where name like '%Tom%'因为:
前缀未知B+Tree 无法定位起点。
只能全表扫描。
6. 范围查询后面的列失效
联合索引:
(name, age, city)查询:
where name='Tom' and age > 20 and city='BJ'执行到:
age > 20之后:
city无法继续利用索引匹配。
原因:
范围查询会破坏索引连续性。
口诀:
范围之后全失效(这里是指索引匹配能力,不是一定完全不用索引)
7. OR 导致索引失效
例如:
where name='Tom' or salary=10000如果:
name有索引 salary没索引MySQL 很可能:
直接全表扫描因为:
走索引 + 全表扫描成本可能更高。
优化:
UNION ALL拆开。
8. 使用 != 或 <>
例如:
where age != 20或者:
where age <> 20数据库发现:
大部分数据都满足需要扫描大量记录。
优化器经常选择:
全表扫描9. NOT IN
例如:
where id not in (1,2,3)通常返回:
绝大部分数据优化器可能放弃索引。
10. NOT EXISTS
类似:
where not exists(...)很多场景索引利用率较低。
11. 数据量太小
例如:
10条数据即使有索引:
全表扫描可能比:
索引查找+回表更快。
优化器会主动放弃索引。
12. 查询结果占比过高
例如:
where gender='男'表:
1000万数据其中:
900万男即使有索引:
idx_gender优化器可能认为:
回表900万次成本太高。
直接:
全表扫描更划算。
高频总结
MySQL 索引失效常见场景包括:
- 不满足联合索引最左前缀原则;
- 对索引列使用函数、计算或表达式;
- 隐式类型转换;
- LIKE 以
%开头;- 联合索引中范围查询后的列无法继续利用索引;
- OR 一侧无索引;
- 使用
!=、<>、NOT IN、NOT EXISTS;- 数据量过小或查询结果占比过高时,优化器主动放弃索引。
本质上,索引失效的原因要么是B+Tree 无法利用有序性定位数据,要么是优化器评估后认为走索引成本高于全表扫描。
