MySQL 索引失效的常见场景与原因分析
很多同学在建表时都会习惯性地给字段加上索引,但执行查询时发现 MySQL 并没有使用索引,依旧是 全表扫描。这是为什么呢?索引并不是万能的,有很多场景会导致 索引失效。今天我们来系统梳理一下这些坑点,帮助你在写 SQL 时少踩坑。
一、对索引字段使用函数或运算
当在 WHERE 条件中对索引字段进行函数处理或运算 时,索引通常会失效。
示例:
-- 索引字段是 create_time
SELECT * FROM user WHERE YEAR(create_time) = 2025;
由于 YEAR()
对字段做了函数计算,MySQL 无法利用原有索引,只能进行全表扫描。
正确写法:
SELECT * FROM user
WHERE create_time >= '2025-01-01' AND create_time < '2026-01-01';
👉 避免在索引字段上做运算,把计算移到常量一侧。
二、模糊查询导致索引失效
LIKE
查询中:
- 前缀匹配能用索引(
LIKE 'abc%'
) - 前置通配符会失效(
LIKE '%abc'
)
示例:
SELECT * FROM user WHERE name LIKE '%Tom'; -- 索引失效
SELECT * FROM user WHERE name LIKE 'Tom%'; -- 可以用索引
优化思路:
- 如果必须做前置模糊,可以考虑 全文索引 或者 ES(Elasticsearch)。
三、索引列出现隐式类型转换
当 索引字段与查询条件类型不一致 时,MySQL 会进行隐式转换,从而导致索引失效。
示例:
-- 假设 user_id 是字符串类型(varchar)
SELECT * FROM user WHERE user_id = 123; -- 索引失效
由于 123
是整型,MySQL 会将 user_id
转换为整型比较,索引失效。
正确写法:
SELECT * FROM user WHERE user_id = '123';
四、组合索引未遵循最左前缀原则
组合索引 (a, b, c)
,查询时必须按照最左前缀顺序使用。
示例:
CREATE INDEX idx_abc ON user(a, b, c);-- 能用索引
SELECT * FROM user WHERE a = 1;
SELECT * FROM user WHERE a = 1 AND b = 2;-- 不能用索引
SELECT * FROM user WHERE b = 2;
SELECT * FROM user WHERE c = 3;
👉 记住:索引列的使用顺序非常重要。
五、OR 条件可能导致索引失效
当 OR
条件中的某个字段未建立索引时,MySQL 往往会放弃整个索引,而进行全表扫描。
示例:
SELECT * FROM user WHERE name = 'Tom' OR age = 20;
若 name
有索引但 age
没有,则索引可能完全失效。
解决方案:
- 给多个字段都加上索引;
- 或者拆分 SQL,用
UNION
合并。
六、范围查询阻断后续索引
组合索引中如果出现范围查询(>
, <
, BETWEEN
),后续字段的索引可能无法使用。
示例:
-- 组合索引 (a, b, c)
SELECT * FROM user WHERE a = 1 AND b > 5 AND c = 10;
这里 a
可以用索引,b
也可以,但 c
可能无法利用索引。
👉 避免在组合索引中间使用范围条件。
七、NOT、!=、<> 等条件
对于 !=
、<>
、NOT IN
等否定条件,索引一般不会生效。
示例:
SELECT * FROM user WHERE age != 20;
因为 MySQL 无法直接通过索引快速排除,只能扫描更多数据。
优化思路:改写为范围查询:
SELECT * FROM user WHERE age < 20 OR age > 20;
八、IS NULL 与 IS NOT NULL
IS NULL
一般可以使用索引。IS NOT NULL
通常会导致索引失效,因为需要扫描大部分数据。
示例:
SELECT * FROM user WHERE age IS NULL; -- 可能用索引
SELECT * FROM user WHERE age IS NOT NULL; -- 索引可能失效
九、字段区分度太低
索引的效果与字段的 基数(区分度) 有关。
例如:性别字段只有 男/女
两种值,索引意义不大,MySQL 可能直接放弃索引。
判断区分度:
SELECT COUNT(DISTINCT col) / COUNT(*) FROM table;
区分度过低的字段,不适合建立索引。
十、强制使用索引失败
有些情况下即便你使用 FORCE INDEX
,MySQL 也可能不使用索引,因为优化器认为 全表扫描更快。
这往往出现在小表或索引选择性不佳的场景。
总结
MySQL 索引失效的常见原因可以总结为以下几类:
- 写法问题:函数/运算、模糊查询、隐式转换。
- 索引设计问题:组合索引未遵守最左前缀、范围查询阻断后续索引。
- 查询逻辑问题:OR、!=、NOT IN、IS NOT NULL。
- 数据特点问题:字段区分度太低、小表全表扫描更快。
👉 建议开发中多用 EXPLAIN
查看执行计划,结合实际数据分布判断索引是否合理使用。