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索引失效的常见场景总结一下:
- 对索引列使用函数或运算
- 不满足最左前缀原则
- like以%开头
- 使用 != 或 <>
- 使用or连接条件
- 隐式类型转换
- 使用not in、not exists
- 表太小,优化器选择全表扫描
写SQL的时候一定要注意避开这些坑,写完之后用EXPLAIN检查一下索引有没有生效。这样你的数据库查询性能才能真正提上去。
遇到问题加QQ23979811 协助处理