Skip to content

MySQL 索引为什么会失效?如何用 EXPLAIN 判断 SQL 有没有用好索引? ​

🧑‍💻 面试官:SQL 明明有索引,为什么还是慢?

🙋‍♂️ 我:可能条件无法有效使用索引,也可能优化器认为别的访问方式更便宜。

🧑‍💻 面试官:EXPLAIN 的 key 有值,是不是就说明优化成功?

🙋‍♂️ 我:还得看扫描范围和回表,可能只是扫描了整个索引。

🧑‍💻 面试官:type 是 ALL,但只查一张很小的表。你会马上强制它走索引吗?

不要把「用了索引」当成目标,要看索引有没有减少真正昂贵的扫描与读取。

面试速答(60 秒版) ​

所谓索引失效,实际要分清两种情况:查询条件无法利用索引有效定位,以及索引可用,但优化器没有选择它。

例如,对索引列做函数处理、发生某些隐式转换,或者字符串查询以通配符开头,可能无法按原索引缩小范围。联合索引还要结合列顺序和具体条件判断,不能只背“有某个操作就一定失效”。

查看 EXPLAIN 时,要一起看实际选择的 key、访问类型、预计扫描行数和附加操作。key 不为空,也可能只是完整索引扫描;需要很多回表时,未必比全表扫描便宜。

最后再用真实数据和合适的执行分析确认。EXPLAIN ANALYZE 会真正执行查询,因此不能对高成本或有风险的语句随意运行。

可用却没选和使用却扫描多

知识点详解:先区分“不能定位”和“不想选” ​

一个日期函数,为什么改变了查找方式? ​

假设订单表在 created_at 上有普通索引。我们想查询某一天的订单,写成:

sql
SELECT id, created_at
FROM orders
WHERE DATE(created_at) = '2026-10-01';

普通索引按原来的时间值排序,而条件要求先把每行时间变成日期再比较。数据库不一定能直接利用这个索引形成高效范围。

可以把条件改成同一时区、同一日期含义下的范围:

sql
SELECT id, created_at
FROM orders
WHERE created_at >= '2026-10-01 00:00:00'
  AND created_at  

> > 
- 先分类:条件不能有效定位,或优化器不选择。
> > 
- 联合索引:理解排序顺序,不背绝对“失效清单”。
> > 
- key 有值:不代表扫描少,也不代表没有回表。
> > 
- 估算与实际:rows 是估算,ANALYZE 会执行。
> > 
- 优化目标:结果不变,减少实际代价,而非强行出现索引名字。
> >

基于 VitePress 构建 | 记录真实开发与 AI 协作过程