核心原则:索引列要保持“原生值”,不能被函数、运算、隐式转换破坏;不走索引范围、前缀规则、优化器判断全表更快时也会失效
1. 索引列上做运算、函数、类型转换
-- 失效:对索引列用函数 select * from t where date(create_time) = '2026-11-04'; -- 失效:索引列做算术运算 select * from t where id + 1 = 100;✅ 正确写法:把运算放到常量侧
select * from t where create_time >= '2026-11-04' and create_time < '2026-11-05'; select * from t where id = 99;2. 隐式类型转换(高频踩坑)
字段phone varchar(11),建了索引:
-- 失效!字符串字段和数字比较,mysql把phone转数字 select * from t where phone = 13800138000; -- 有效 select * from t where phone = '13800138000';本质:索引列被转换了,而不是常量。
这能叫转换吗,不就是类型不匹配吗
它确实是「类型不匹配」,但 MySQL 内部自动做了隐式转换,并且转换发生在索引字段上,才导致索引失效,这两个说法不冲突。
3. 复合索引,不满足最左前缀原则
联合索引idx(a,b,c)
- ✅
where a=1、where a=1 and b=2、where a=1 and b=2 and c=3 - ❌
where b=2、where b=2 and c=3、where c=3注意:中间断了就断了:
where a=1 and c=3→ 只能用到a这一列索引,c失效
4. like 以通配符开头
-- 失效 select * from t where name like '%张三'; select * from t where name like '%张三%'; -- 有效 select * from t where name like '张三%';想前后模糊检索:用全文索引、ES,普通B+树做不到
5. or 连接条件,一侧没有索引
-- 假设a有索引,b无索引 → 整个索引失效,走全表 select * from t where a=1 or b=2;✅ 优化:改成 union all
select * from t where a=1 union all select * from t where b=2;补充:or两边都有独立索引,优化器有可能走索引合并(index_merge),但不稳定
6. 范围查询(> < >= <= between)之后的列失效
联合索引idx(a,b,c)
-- a等值,b范围,后面c失效!只能用到a、b select * from t where a=1 and b>10 and c=5;规则:联合索引中,范围条件右边的列,不能使用索引
7. not in、!=、<>、not exists(优化器大概率放弃索引)
select * from t where id not in (1,2,3); select * from t where name != 'abc';不是100%一定失效,但是很大概率走全表扫描;数据分布极端离散才可能走索引,不依赖
8. is not null 不一定走索引,is null 可以走
索引本身不存NULL,当大量数据是is not null,选择性差,优化器会放弃索引。
-- 有可能失效,看选择性 select * from t where name is not null; -- 可以走索引 select * from t where name is null;9. 优化器认为全表扫描更快(选择性太低)
比如性别字段,只有男/女,基数很低,就算建索引:
select * from t where sex = '男';数据量很大、符合条件行占比很高(一般超过20%左右,阈值不固定),优化器直接放弃索引,走全表。
10. 其他冷门
- 分区键问题:分区表条件没带上分区键,无法定位分区,索引失效
- 优化器成本计算、索引统计信息不准(analyze table 更新统计)
- 使用 force index 强制索引只是告诉优化器,不代表索引本身可用