ORDER BY字段必须严格匹配索引最左前缀才能避免filesort;需覆盖查询字段、注意ASC/DESC显式声明(8.0+)、LIMIT影响优化器选择,EXPLAIN中出现Using filesort即未走索引排序。

ORDER BY 字段必须是索引的最左前缀
MySQL 只有在 ORDER BY 子句中的字段,**严格匹配索引定义的最左连续列**时,才能利用索引避免文件排序(Using filesort)。哪怕只多一个非连续字段,或顺序不一致,优化器大概率会放弃索引排序。
比如有复合索引 INDEX idx_user_status_created (status, created_at):
-
ORDER BY status, created_at✅ 走索引 -
ORDER BY status✅ 走索引(最左前缀) -
ORDER BY created_at❌ 不走索引排序(跳过status) -
ORDER BY status DESC, created_at ASC❌ 多数版本不走索引(升序/降序混用,8.0+ 支持但需显式定义)
避免 SELECT * 与覆盖索引冲突
即使 ORDER BY 字段有索引,如果查询中包含大量非索引字段(如 SELECT *),MySQL 仍可能放弃索引排序——因为回表成本高,优化器觉得全表扫描 + filesort 更快。
更稳妥的做法是:让索引「覆盖」查询所需全部字段(即覆盖索引)。
- 原语句:
SELECT id, name, email FROM users WHERE status = 'active' ORDER BY created_at; - 对应索引应为:
INDEX idx_status_created_cover (status, created_at, id, name, email) - 这样既满足
WHERE status = ...过滤,又满足ORDER BY created_at,还能避免回表
注意 LIMIT 对执行计划的影响
LIMIT 本身不会让 MySQL 自动选择排序索引,但它会影响优化器对“取前 N 行是否值得用索引排序”的判断。尤其当 WHERE 条件匹配行数远大于 LIMIT 值时,使用索引排序 + LIMIT 往往显著更快。
但要注意:如果 WHERE 条件太宽泛(例如无有效过滤),MySQL 可能仍选错执行路径。
- 检查是否真的用了索引排序:
EXPLAIN SELECT * FROM orders WHERE user_id > 1000 ORDER BY created_at DESC LIMIT 20;
- 关键看
Extra列:出现Using filesort就说明没走索引排序 - 若
type是index且key显示用了排序索引,通常靠谱
ASC/DESC 在索引定义中要显式声明(MySQL 8.0+)
MySQL 8.0 之前,索引默认按升序存储,ORDER BY ... DESC 无法利用普通 B-Tree 索引做排序;8.0+ 支持在建索引时指定方向,但必须显式写出,否则仍不生效。
错误写法:CREATE INDEX idx_time ON events(created_at); → 无法支持 ORDER BY created_at DESC 排序优化
正确写法:
CREATE INDEX idx_time_desc ON events(created_at DESC);
注意:如果同时需要 ASC 和 DESC 查询,目前只能建两个索引,或接受其中一种走 filesort。
索引排序不是“建了就能用”,它高度依赖字段顺序、查询写法、版本特性,以及优化器对成本的估算。最容易被忽略的是:明明建了索引,EXPLAIN 却显示 Using filesort —— 先确认 ORDER BY 是否命中最左前缀,再查是否因 SELECT 字段过多导致放弃索引,最后看版本和排序方向是否匹配。










