key 是实际使用的索引,possible_keys 是候选索引;key 为 null 表示未走索引,possible_keys 为空可能因类型不匹配导致隐式转换;type=range 不一定快,需结合 rows 和 filtered 判断;order by 走索引需满足顺序一致且覆盖最左前缀;using index condition 是 icp 优化,减少回表但不改变扫描范围。

怎么看 EXPLAIN 里 key 和 possible_keys 的区别
key 是 MySQL 实际用上的索引,possible_keys 是它“觉得能用”的候选索引——但选不选、怎么选,得看统计信息、数据分布和 WHERE 条件的写法。
常见错误现象:possible_keys 有多个,key 却是 NULL,说明没走索引;或者 key 是个低效索引(比如只用上了联合索引的前缀),但 possible_keys 里明明有更匹配的。
- WHERE 中用了函数或表达式(如
WHERE YEAR(created_at) = 2023)会直接让所有索引失效,key变成NULL - 联合索引
(a, b, c),查询条件是WHERE b = 1 AND c = 2,key仍为NULL—— 因为没覆盖最左列a -
possible_keys为空,不一定代表没建索引,可能是字段类型不一致(比如id是BIGINT,但 WHERE 里写了字符串'123',触发隐式转换)
type=ALL 和 type=range 之间差的不只是扫描行数
type=ALL 是全表扫描,type=range 是索引范围扫描——但别光看 type,还得看 rows 和 filtered。
使用场景:当 rows 显示 50 万,哪怕 type 是 range,实际也比一个 type=ref 扫 5000 行更慢;而 filtered=10.00 意味着 MySQL 预估只有 10% 的行满足条件,剩下 90% 白扫了。
-
type=range不等于“快”,如果索引区分度极低(比如status只有 0/1 两个值),MySQL 可能宁愿全表扫也不走索引 -
type=ref通常比range更优,但它要求等值匹配且索引列在 WHERE 中独立出现(不能包裹在函数里) - 注意
Extra字段:出现Using filesort或Using temporary时,即使 type 是ref,性能也可能断崖下跌
ORDER BY 为什么有时不走索引
MySQL 只有在满足“索引顺序与 ORDER BY 完全一致 + WHERE 条件覆盖索引最左前缀”时,才可能避免排序操作。
参数差异:ASC/DESC 混用(如 ORDER BY a ASC, b DESC)在 MySQL 8.0 之前无法利用索引排序;8.0+ 支持,但前提是建索引时显式声明方向,比如 INDEX(a ASC, b DESC)。
- SELECT 的字段如果不在索引中,MySQL 可能放弃索引排序,改用文件排序(
Using filesort) - WHERE 和 ORDER BY 涉及不同字段,比如
WHERE category = ? ORDER BY created_at,除非有联合索引(category, created_at),否则大概率触发排序 - 覆盖索引能缓解这个问题:如果
SELECT id, name且索引是(category, created_at, id, name),就不用回表,排序成本更低
慢查询里 Using index condition 是好事还是坏事
这是 MySQL 的 ICP(Index Condition Pushdown)优化,意思是把部分 WHERE 条件下推到存储引擎层,在读取索引节点时就过滤,减少回表次数——通常是好事,但容易被误读为“已经全靠索引搞定”。
性能影响:它只优化“索引扫描后的过滤”,不改变扫描范围。如果 rows 很大,ICP 再强也救不了。
- ICP 在
type=range或type=ref下生效,type=ALL不触发 - 它对
LIKE 'abc%'有效,但对LIKE '%abc'无效(没法用索引做前缀匹配) - 如果看到
Using index condition却依然慢,优先检查rows是否过大,而不是怀疑 ICP 失效
真正卡住性能的,往往不是某一行 EXPLAIN 输出,而是索引设计是否贴合查询模式、数据倾斜是否严重、统计信息是否过期——这些没法靠看一眼执行计划就发现。











