索引“存在”不等于“生效”,explain中key非null仅表示选了索引,而rows大、type为range/index、extra含using filesort/temporary说明实际未高效使用。

慢查询报警响了,90%的情况不是索引没建,而是索引建得不对、用得不巧,或者被悄悄绕过了。
为什么EXPLAIN显示用了索引,但还是慢?
这是最常被忽略的陷阱:索引“存在”不等于“生效”。EXPLAIN里key列非NULL,只说明MySQL选了某个索引;但rows值巨大、type是range或index、Extra出现Using filesort或Using temporary,就说明它只是“勉强用了”,实际仍要扫描大量数据。
- 常见诱因:
WHERE created_at > '2025-01-01'在联合索引(status, created_at)中,只能用上status前缀,created_at部分失效 - 函数操作直接废掉索引:
WHERE DATE(order_time) = '2025-06-01'→ 改成WHERE order_time >= '2025-06-01' AND order_time - 隐式类型转换:
user_id是INT,但SQL写了WHERE user_id = '1001',触发字符串转数字,索引可能失效
哪些WHERE条件组合该建联合索引?
别给每个字段单独建索引。MySQL一次查询通常只用一个索引(index merge极少见且不可控),真正高效的是按查询模式设计复合索引。
- 优先覆盖高频查询的
WHERE+ORDER BY+SELECT字段:比如常查WHERE status = ? AND category = ? ORDER BY created_at DESC,就建INDEX idx_status_cat_created (status, category, created_at) - 把等值条件放前面,范围条件放后面:顺序必须是
WHERE a = 1 AND b = 2 AND c > 100,对应索引(a, b, c)才有效;反过来写,c就断掉了最左前缀 - 避免冗余索引:
INDEX (a)和INDEX (a, b)同时存在,前者基本无用,删掉
怎么验证索引是否真起作用?
别只信EXPLAIN的静态计划,要看真实执行时的资源消耗。
- 查
information_schema.PROCESSLIST,看慢查询执行中是否持续占用连接、CPU或IO - 对比
Rows_examined:优化前后这个值应明显下降。如果从50万降到500,哪怕执行时间只降了0.3秒,也是本质改善 - 用
SHOW PROFILE FOR QUERY N(需先SET profiling = 1)看各阶段耗时,确认瓶颈是否从“Sending data”转移到了“Copying to tmp table”之类更可控的环节
真正卡住人的,往往不是“要不要加索引”,而是WHERE里那个不起眼的函数调用、JOIN时字段类型的微小不匹配、或者ORDER BY字段和索引顺序差了一位——这些细节不会报错,但会让索引彻底失效,还很难复现。











