ORDER BY 走索引需满足最左前缀原则:排序字段必须构成索引的连续最左前缀;WHERE 等值+ORDER BY 后续字段可生效,范围查询后排序失效;ASC/DESC 方向须与索引定义严格一致;隐式转换、函数操作、非覆盖查询等均会导致 filesort。

ORDER BY 字段没走索引?先看是否满足最左前缀
MySQL 的 ORDER BY 能用上索引的前提,是排序字段构成索引的**最左前缀连续子集**。不是“只要字段有索引就行”,而是必须和索引定义顺序严格匹配。
- 比如联合索引是
(a, b, c),那么ORDER BY a、ORDER BY a,b、ORDER BY a,b,c可以走索引;但ORDER BY b或ORDER BY a,c就不行(c跳过了b) - 如果 WHERE 条件用了
a = ?,再ORDER BY b,c,这个组合在(a,b,c)索引里依然能走索引——因为a是等值过滤,后续b,c构成连续最左前缀 - 但如果 WHERE 是
a > ?,再ORDER BY b,就大概率失效:范围查询后的字段无法用于排序索引下推
ASC/DESC 混用导致索引失效的典型场景
MySQL 8.0 之前不支持混合排序方向的索引利用;8.0+ 虽支持,但前提是索引定义时就明确声明了方向,且查询中的 ORDER BY 必须完全一致。
- 建索引写成
INDEX idx(a ASC, b DESC),那只有ORDER BY a ASC, b DESC能用上;换成ORDER BY a DESC, b ASC就会回表或文件排序 - 没显式声明方向时(如
INDEX idx(a,b)),默认全是ASC,此时ORDER BY a DESC, b DESC在 8.0+ 可用,但ORDER BY a ASC, b DESC仍不可用 -
EXPLAIN中看到Extra: Using filesort就说明排序没走索引——别只盯着key列是否非 NULL
WHERE + ORDER BY 组合下,避免隐式类型转换
字符串字段用数字条件查询、或字段带函数包装,都会让索引在排序阶段“断连”。
-
WHERE status = '1'对status VARCHAR字段是安全的;但写成WHERE status = 1会触发隐式转换,可能导致ORDER BY created_at无法复用联合索引 -
ORDER BY UPPER(name)或WHERE DATE(create_time) = '2024-01-01'直接让对应字段的索引失效——函数操作无法走索引定位,更别说排序了 - 时间范围查询慎用
BETWEEN包含边界,若实际只需要最新 N 条,用WHERE create_time > ? ORDER BY create_time LIMIT N更容易命中索引
覆盖索引 + ORDER BY 的性能临界点
即使 ORDER BY 走了索引,如果 SELECT 的字段太多、或存在大字段(如 TEXT),MySQL 可能放弃索引排序,改用回表 + filesort。
- 用
SELECT *配合ORDER BY id,哪怕id是主键,也可能因回表代价高而选错执行计划 - 优先尝试覆盖索引:把
WHERE条件字段、ORDER BY字段、以及 SELECT 中用到的字段,全包含进一个联合索引(注意顺序) - 如果排序结果集很大(比如
LIMIT 10000, 20),即使走了索引,也要扫描前 10020 行——这时候考虑用游标分页(WHERE id > ? ORDER BY id LIMIT 20)更稳
真正卡住性能的往往不是“有没有索引”,而是 WHERE 和 ORDER BY 共同作用时,索引能否一次性支撑过滤+排序+回表三件事。检查 EXPLAIN 的 key_len 和 Extra,比背口诀管用得多。



















