ORDER BY 慢的主因是未走索引导致 Using filesort,而非排序本身;关键取决于查询是否满足索引有序性,即 ORDER BY 列顺序、方向是否与可用索引严格匹配。

ORDER BY 为什么慢?不是因为排序本身,而是没走索引
MySQL 执行 ORDER BY 时,如果无法利用索引完成排序,就会触发 Using filesort——这不是在磁盘上真建个文件排序,而是用内存或临时文件做二次排序,I/O 和 CPU 开销陡增。关键不在“数据多”,而在“能不能跳过排序步骤”。
真正决定快慢的,是查询是否满足「索引有序性」:即 ORDER BY 的列顺序、方向(ASC/DESC),是否与某个可用索引的定义严格匹配。
- 复合索引
INDEX (a, b, c)可以加速ORDER BY a, b或ORDER BY a, b, c DESC(注意:所有DESC必须一致,MySQL 8.0+ 支持混合方向,但老版本不认a ASC, b DESC) - 如果
WHERE条件用了a = ?,再ORDER BY b, c,这个索引依然有效;但如果WHERE是a > ?,那只有ORDER BY b能复用索引,ORDER BY b, c就失效了 -
SELECT *+ORDER BY配合大偏移量(如LIMIT 10000, 20)会强制扫描前 10020 行,即使走了索引,性能也差——这不是排序问题,是分页逻辑缺陷
如何确认 MySQL 有没有用索引排序?看执行计划里的 Extra
别猜,直接 EXPLAIN。核心就盯 Extra 字段:
- 出现
Using filesort→ 没走索引排序,正在额外排序 - 没有
Using filesort,且key列显示用了某个索引 → 排序被索引覆盖,OK - 如果
key是NULL,但也没Using filesort?那是走了主键聚簇索引(比如全表扫描按主键顺序返回),不算优化,只是碰巧有序
示例:
EXPLAIN SELECT * FROM orders WHERE user_id = 123 ORDER BY created_at DESC;如果
Extra 是空的,且 key 显示用了 idx_user_created(假设该索引是 (user_id, created_at)),说明排序被消除;如果出现 Using filesort,大概率是索引没建对,或者 created_at 上单独有索引但没和 user_id 组合。
哪些常见写法会悄悄让索引排序失效
不是语法错,而是语义破坏了索引的有序前提:
-
ORDER BY ABS(id)、ORDER BY UPPER(name):函数操作导致无法直接比较索引值 -
ORDER BY a + b:表达式计算,索引字段被“包裹”了 -
ORDER BY a ASC, b DESC(在 MySQL 5.7 及更早版本):复合索引只支持全部升序或全部降序,混合方向不利用索引排序 -
WHERE a IN (1,2,3) ORDER BY b:范围条件后跟排序列,除非是覆盖索引且b是索引第二列,否则大概率Using filesort - 连接查询中
ORDER BY引用的是非驱动表的列,而没在该表上建合适索引
覆盖索引 + 排序:少一次回表,顺便解决排序
如果 SELECT 的字段都能被同一个索引“覆盖”,MySQL 就不用回主键查数据,还能顺带用这个索引完成排序——一箭双雕。
比如查询:
SELECT order_id, status, created_at FROM orders WHERE user_id = 456 ORDER BY created_at DESC;建联合索引
INDEX (user_id, created_at, order_id, status)(把 WHERE 列放最左,ORDER BY 列紧随其后,最后是 SELECT 中要的其他字段),就能同时满足过滤、排序、覆盖三个条件。
注意:字段顺序不能乱。把 created_at 放在 user_id 前面,整个索引对这个查询就废了;把 status 放在 created_at 前面,排序也会中断。
索引不是越多越好。每个额外字段都增加索引体积和写开销,尤其是高更新频率的表。优先保 WHERE + ORDER BY 的最小组合,再看能否捎带覆盖 SELECT 列。










