这是开发、面试、SQL 优化最常考、最容易踩坑 的知识点,我直接给你最清晰、最实用、能直接对照代码的版本,遇到慢 SQL 一查就懂。
一、最常见:索引列上使用 函数 / 运算
只要对索引列做任何计算、函数处理,索引直接失效!
sql
-- 索引失效
WHERE YEAR(create_time) = 2024
WHERE price + 1 = 100
WHERE LEFT(name,2) = '张'
-- 索引生效
WHERE create_time >= '2024-01-01'
WHERE price = 99
WHERE name LIKE '张%'
二、模糊查询 % 开头
sql
-- 失效
WHERE name LIKE '%张三'
WHERE name LIKE '%三%'
-- 生效
WHERE name LIKE '张三%'
原因:% 在前无法使用 B+ 树有序匹配。
三、隐式类型转换(最容易忽略)
索引列是字符串,查询传数字 → 索引失效!
sql
-- phone 是 varchar 索引列
WHERE phone = 13800138000 -- 失效(MySQL 做隐式转换)
WHERE phone = '13800138000' -- 生效
四、联合索引违反最左前缀原则
联合索引 (a,b,c):
sql
WHERE b=? -- 失效
WHERE c=? -- 失效
WHERE a=? AND c=? -- 只命中 a,b 断层
五、使用 OR 但有一列没有索引
sql
-- name 有索引,age 无索引 → 全表扫描
WHERE name='张三' OR age=20
OR 必须两边都有索引才会走索引。
六、判断 != / <> / NOT IN / NOT EXISTS
sql
WHERE status != 1
WHERE id NOT IN (1,2,3)
大概率导致索引失效,优化器直接选择全表扫描。
七、判断 IS NOT NULL
sql
WHERE name IS NOT NULL -- 索引失效
IS NULL 可以走索引,IS NOT NULL 基本不走。
八、联合索引中,范围列后面的列无法命中
索引 (a, b, c):
sql
WHERE a=1 AND b>10 AND c=100
- a、b 命中索引
- c 失效(因为 b 是范围查询,截断了)
口诀:范围之后,索引失效
九、ORDER BY 不符合索引规则
排序字段和索引顺序不一致 → 索引失效。
sql
-- 索引 (a,b)
ORDER BY b -- 失效
ORDER BY a DESC, b ASC -- 失效(混合排序)
十、数据量太少,优化器选择全表扫描
表只有几十行,MySQL 觉得全表扫描比走索引更快,直接放弃索引。
十一、使用 SELECT * 导致无法使用覆盖索引
sql
SELECT * FROM user WHERE name='张三'
如果能写成覆盖索引,速度会快很多:
sql
SELECT name FROM user WHERE name='张三'
十二、索引区分度太低
性别、状态(0/1)这种重复值极多的列,建了索引也不会用。
最精简记忆版(面试直接背)
- 索引列函数、运算
- like %xx
- 隐式类型转换
- 联合索引最左前缀不匹配
- OR 有一列无索引
- != / not in / not exists
- is not null
- 范围查询后面列失效
- order by 不按索引
- 数据太少优化器放弃索引
总结
只要你写 SQL 时记住一句话:
不计算、不函数、不隐式转换、最左前缀不乱写,99% 的索引都不会失效。