MySQL 索引失效场景总结:面试必问的 10 种情况,你踩过几个?

0 阅读5分钟

前言

在 MySQL 面试中,除了索引原理,索引失效是另一个高频考点。很多开发者自以为建了索引就能万事大吉,结果一条 SQL 就把索引"干废"了,导致全表扫描,性能急剧下降。

今天我们就来盘点最常见的 10 种索引失效场景,用最简短的例子说明原因,并附上避免方法。看完这篇文章,下次面试官问"什么情况下索引会失效",你就能对答如流。


如何判断索引是否失效?

在开始之前,先复习一个工具:EXPLAIN。执行 EXPLAIN SELECT ... 后,重点关注两个字段:

  • type:访问类型,从好到差依次为 system > const > eq_ref > ref > range > index > ALL。如果出现 ALL(全表扫描),说明索引大概率失效了。
  • key:实际使用的索引,为 NULL 表示没用到索引。

🔥 索引失效的 10 大场景

1. 违反最左前缀原则(针对联合索引)

错误示例

-- 假设联合索引 idx_a_b_c (a, b, c)
SELECT * FROM t WHERE b = 2;  -- ❌ 索引失效(跳过了 a)
SELECT * FROM t WHERE b = 2 AND c = 3;  -- ❌ 索引失效

正确示例

SELECT * FROM t WHERE a = 1 AND b = 2;  -- ✅ 使用索引(用到 a、b)
SELECT * FROM t WHERE a = 1;  -- ✅ 使用索引(用到 a)

原理:联合索引 (a, b, c) 相当于创建了 (a)(a,b)(a,b,c) 三个索引。查询条件必须从最左边的列开始,跳过左边列直接查右边会导致索引失效。


2. 使用 != 或 <> 操作符

错误示例

SELECT * FROM user WHERE age != 18;  -- ❌ 索引失效

正确示例

SELECT * FROM user WHERE age < 18 OR age > 18;  -- ✅ 可能使用索引

原理:不等于操作符通常会让索引失效(有时优化器会进行索引扫描,但效率低)。


3. 索引列参与了函数运算

错误示例

-- 假设 create_time 有索引
SELECT * FROM order WHERE DATE(create_time) = '2025-03-17';  -- ❌ 索引失效

正确示例

SELECT * FROM order WHERE create_time >= '2025-03-17' AND create_time < '2025-03-18';  -- ✅ 使用索引

原理:对索引列使用函数(包括隐式类型转换)会导致索引失效。


4. 类型隐式转换

错误示例

-- 假设 mobile 字段是 VARCHAR 类型,且建有索引
SELECT * FROM user WHERE mobile = 13888888888;  -- ❌ 索引失效

正确示例

SELECT * FROM user WHERE mobile = '13888888888';  -- ✅ 使用索引

原理:如果字段类型与查询值类型不一致,MySQL 会进行隐式转换,导致索引失效。


5. LIKE 以通配符 % 开头

错误示例

SELECT * FROM article WHERE title LIKE '%MySQL';  -- ❌ 索引失效
SELECT * FROM article WHERE title LIKE '%MySQL%';  -- ❌ 索引失效

正确示例

SELECT * FROM article WHERE title LIKE 'MySQL%';  -- ✅ 使用索引

原理LIKE 模糊查询时,如果通配符在开头,索引失效。


6. OR 连接的条件不是全部有索引

错误示例

-- 假设 age 有索引,name 没有索引
SELECT * FROM user WHERE age = 18 OR name = '张三';  -- ❌ 索引失效

正确示例

-- 方案1:为 OR 涉及的每个列都创建索引
SELECT * FROM user WHERE age = 18 OR name = '张三';

-- 方案2:改用 UNION
SELECT * FROM user WHERE age = 18
UNION ALL
SELECT * FROM user WHERE name = '张三';

原理:如果 OR 前后的条件中,有一个列没有索引,另一个列即使有索引也不会使用。


7. 索引列参与了计算或表达式

错误示例

SELECT * FROM product WHERE price * 0.8 > 100;  -- ❌ 索引失效

正确示例

SELECT * FROM product WHERE price > 100 / 0.8;  -- ✅ 使用索引

原理:在索引列上进行算术运算或其他表达式操作,索引失效。


8. 使用 NOT IN 或 NOT EXISTS

错误示例

SELECT * FROM user WHERE id NOT IN (SELECT user_id FROM order);  -- ❌ 可能索引失效

正确示例

-- 用 LEFT JOIN 优化
SELECT user.* FROM user 
LEFT JOIN order ON user.id = order.user_id 
WHERE order.user_id IS NULL;

原理NOT IN 和 NOT EXISTS 可能导致索引失效,尤其是子查询返回大量数据时。


9. 数据分布不均匀,优化器放弃索引

错误示例

-- 假设 status 字段只有两个值:0 和 1,且 90% 的数据 status=1
SELECT * FROM order WHERE status = 1;  -- ❌ 优化器可能不走索引

原理:即使索引可用,如果 MySQL 优化器判断全表扫描比索引更快(比如查询结果占全表很大比例),就会放弃索引。

解决:这种情况通常不需要干预,优化器选择的是最优方案。


10. 索引列是 NULL 值查询

错误示例

SELECT * FROM user WHERE email IS NULL;  -- ❌ 索引失效(取决于数据分布)

正确示例

-- 建议:设计表时尽量避免列允许为 NULL
CREATE TABLE user (
    id INT PRIMARY KEY,
    email VARCHAR(255) NOT NULL  -- 设置为 NOT NULL
);

原理:对于 IS NULL 或 IS NOT NULL,索引是否失效取决于 MySQL 版本和数据分布。


✅ 总结:索引失效避坑指南

场景示例解决
违反最左前缀WHERE b=1 (联合索引 a,b)查询条件中包含最左列
使用 != 或 <>WHERE age != 18用 > 和 < 组合代替
索引列参与函数WHERE DATE(create_time)=...改为范围查询
类型隐式转换WHERE mobile=138...字符串加引号
LIKE 以 % 开头WHERE name LIKE '%张'避免前缀 %
OR 条件不全有索引WHERE age=1 OR name='a'所有列建索引或改 UNION
索引列参与计算WHERE price*0.8 >100计算放右边
NOT IN / NOT EXISTSWHERE id NOT IN (…)用 LEFT JOIN 改写
数据分布不均WHERE status=1 (90%数据是1)优化器选择,不用干预
NULL 查询WHERE email IS NULL列设为 NOT NULL

结语

索引失效是 SQL 性能优化的核心问题之一。掌握这些场景,不仅能帮你通过面试,更能在实际工作中写出高效的 SQL。

记住"SQL 是否使用索引,跟数据库版本、数据量、数据选择度都有关系" 。在实际开发中,多用 EXPLAIN 分析执行计划,不要仅凭经验判断。

本文mysql索引失效文档,适合面试和实际开发参考。索引优化是一个持续的过程,随着数据量增长和业务变化,需要不断调整和优化。


欢迎关注我的公众号【SilkyStarter】 ,加入技术交流群,获取更多 MySQL、Java 干货!