MySQL索引失效的四大主因:WHERE中对索引列使用函数或表达式;联合索引未遵循最左前缀原则;LIKE左模糊、OR混合无索引列、IS NOT NULL等破坏有序性操作;字段类型不匹配或数据分布偏斜导致优化器放弃索引。

WHERE 里用了函数或表达式,索引直接不走
MySQL 的索引存的是字段原始值,一旦你在 WHERE 条件里对索引列做计算或调用函数,InnoDB 就没法拿索引树里的值去直接比对了。比如 WHERE YEAR(create_time) = 2023,哪怕 create_time 有索引,也白搭。
- ❌ 错误写法:
WHERE DATE(order_date) = '2024-04-21'、WHERE price * 1.1 > 100、WHERE LEFT(name, 3) = 'Joh' - ✅ 正确改法:把函数“挪”到右边,转成范围查询——
WHERE order_date >= '2024-04-21' AND order_date 、WHERE price > 100 / 1.1 - ⚠️ 注意:
CAST()或隐式转换(比如字符串字段查数字)本质也是函数,同样失效,别以为只是“少个引号”而已
联合索引没按最左前缀用,后边全作废
复合索引 (a, b, c) 是按 B+ 树的层级顺序组织的:先排 a,a 相同再排 b,b 相同再排 c。跳过前面的列,就等于找不到入口,整棵树都用不上。
- ❌ 失效场景:
WHERE b = 2 AND c = 3(没 a)、WHERE a = 1 AND c = 3(跳过 b)、WHERE a > 10 AND b = 5(a 是范围,b 后续失效) - ✅ 解法不是硬凑条件,而是看高频查询反推索引设计:如果常查
city和district,就建(city, district),别为了“覆盖更多”硬塞个(country, city, district)却总只查后两列 - ? 补充:范围查询(
BETWEEN、>、LIKE 'abc%')会让其后的索引列失效,不是“不能建”,是“建了也用不上”
LIKE 左模糊、OR 混合无索引列、IS NOT NULL 都容易踩坑
这些看着像语法糖的操作,底层其实破坏了索引的有序性或可预估性,优化器一算——不如全表扫快。
- ❌
WHERE name LIKE '%john':B+ 树没法从中间开始找,必须逐行扫 - ❌
WHERE a = 1 OR b = 2:如果只有a有索引、b没索引,MySQL 往往放弃索引合并,直接全表扫描 - ❌
WHERE status IS NOT NULL:B+ 树默认不存NULL值,查非空就得挨个判断,除非你显式为该列建索引(且字段本身允许NULL) - ✅ 应对:左模糊换全文索引或应用层反向索引;
OR拆成UNION ALL或给所有涉及列补索引;IS NOT NULL查询频繁就加NOT NULL约束,或单独建索引
类型不一致、数据太偏斜,优化器主动弃疗
有时候索引明明存在、写法也没错,但执行计划里 type 还是 ALL——不是 bug,是优化器在“理性放弃”。
- ❌ 字段是
VARCHAR,却写WHERE phone = 13800138000:MySQL 自动加CAST(phone AS UNSIGNED),索引失效 - ❌ 某个值占全表 95%,比如
status = 'active',优化器发现走索引还要回表 95% 的记录,不如直接扫聚簇索引 - ✅ 验证手段只有一个:
EXPLAIN看key是否命中、rows是否合理、Extra有没有Using filesort或Using temporary - ⚠️ 最容易被忽略的一点:统计信息过期。大批量导入后没跑
ANALYZE TABLE,优化器还按旧分布估算,可能误判索引价值










