MySQL索引失效常见场景,避免踩坑

前言

做数据库开发的同学都知道,索引是提升查询性能最有效的手段。很多时候一张几百万行的大表,加个合适的索引查询速度就能从几秒变成几毫秒。

但是很多同学不知道的是,就算你加了索引,如果SQL语句写得不对,索引也可能会失效,结果还是全表扫描,查询慢得要命。这篇文章就把MySQL索引失效的常见场景都整理出来,帮你避免踩坑。

常见索引失效场景

1. 对索引列使用函数或运算

这是最常见的索引失效场景。如果你在索引列上用了函数或者做了运算,MySQL就没法用到这个索引了。

失效的写法:

-- 对索引列使用函数
SELECT * FROM users WHERE DATE(create_time) = '2026-10-05';

-- 对索引列做运算
SELECT * FROM orders WHERE amount + 100 > 500;

正确的写法:

-- 改成范围查询
SELECT * FROM users 
WHERE create_time >= '2026-10-05 00:00:00' 
AND create_time < '2026-10-06 00:00:00';

-- 把运算移到右边
SELECT * FROM orders WHERE amount > 400;

2. 不满足最左前缀原则

如果你建的是联合索引(比如 index(a, b, c)),查询的时候必须从最左边的列开始用,否则索引就会失效。

联合索引:index(name, age, city)

失效的写法:

-- 没有用到最左列name
SELECT * FROM users WHERE age = 25;

-- 跳过了中间的列
SELECT * FROM users WHERE name = '张三' AND city = '北京';

正确的写法:

-- 从最左列开始
SELECT * FROM users WHERE name = '张三';

-- 按顺序用
SELECT * FROM users WHERE name = '张三' AND age = 25;

3. 使用like以%开头

如果你的查询条件是 like '%xxx' 或者 like '%xxx%',那索引就会失效,因为MySQL不知道从哪里开始找。

失效的写法:

-- %在前面
SELECT * FROM users WHERE name LIKE '%张%';

-- %在前后都有
SELECT * FROM users WHERE name LIKE '%张';

正确的写法:

-- %在后面,这样可以用到索引
SELECT * FROM users WHERE name LIKE '张%';

如果一定要用前后模糊匹配的话,可以考虑全文索引或者ES搜索引擎。

4. 索引列使用 != 或 <>

如果查询条件用了 != 或者 <>,MySQL优化器会觉得大部分数据都要查,还不如直接全表扫描,所以索引就失效了。

失效的写法:

SELECT * FROM users WHERE status != 1;
SELECT * FROM users WHERE status <> 1;

正确的写法:

-- 改成in或者or
SELECT * FROM users WHERE status IN (0, 2, 3);

5. 使用or连接条件

如果or两边的列不是都有索引,那整个查询就会全表扫描。

失效的写法:

-- name有索引,但是age没有索引
SELECT * FROM users WHERE name = '张三' OR age = 25;

正确的写法:

-- 用union all代替or
SELECT * FROM users WHERE name = '张三'
UNION ALL
SELECT * FROM users WHERE age = 25;

或者给age也加上索引,这样两边都能用索引。

6. 隐式类型转换

这是最坑的一个场景。如果你的索引列是字符串类型,但是查询的时候传了数字,MySQL会自动做类型转换,结果索引就失效了。

失效的写法:

-- 索引列phone是varchar类型,但是传了数字
SELECT * FROM users WHERE phone = 13800138000;

正确的写法:

-- 加引号,传字符串
SELECT * FROM users WHERE phone = '13800138000';

7. 使用not in、not exists

not in 和 not exists 也很容易导致索引失效。因为MySQL优化器对这类反查询的优化做得不太好。

失效的写法:

SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM orders);

正确的写法:

-- 改成left join
SELECT u.* FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.user_id IS NULL;

8. 优化器认为全表扫描更快

还有一种情况是,虽然你写的SQL完全符合索引使用规则,但是MySQL优化器自己判断说"这张表数据太少了,全表扫描比走索引还快",那它就会选择全表扫描。

这种情况是正常的,不用纠结。一般小表全表扫描确实更快。

如何检查索引是否生效

那怎么知道自己写的SQL有没有用到索引呢?很简单,用EXPLAIN关键字看执行计划就行。

EXPLAIN SELECT * FROM users WHERE name = '张三';

重点看这几个字段:

  • type:如果是 ALL 就是全表扫描,说明索引失效了;如果是 ref、eq_ref、const 就说明用到索引了
  • key:这里显示实际用到的索引名,如果是 NULL 说明没用到索引
  • rows:扫描的行数,越大说明效率越低

常见坑

坑1:加了索引但是没生效,以为索引没用

很多同学加了索引之后发现查询还是慢,就觉得索引没用。其实大概率是你的SQL写法有问题,导致索引失效了。一定要先用EXPLAIN看一下执行计划,确认索引有没有被用到。

坑2:联合索引建了不用最左列

很多同学建了联合索引,但是查询的时候没用到最左边的列,结果索引白建了。一定要记住最左前缀原则。

坑3:隐式类型转换导致索引失效

这个最坑,因为写SQL的时候完全看不出来。比如手机号、身份证号这些字符串类型的字段,查询的时候一定要加引号,不然索引就失效了。

总结

MySQL索引失效的常见场景总结一下:

  1. 对索引列使用函数或运算
  2. 不满足最左前缀原则
  3. like以%开头
  4. 使用 != 或 <>
  5. 使用or连接条件
  6. 隐式类型转换
  7. 使用not in、not exists
  8. 表太小,优化器选择全表扫描

写SQL的时候一定要注意避开这些坑,写完之后用EXPLAIN检查一下索引有没有生效。这样你的数据库查询性能才能真正提上去。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 08:27