ORDER BY 实际执行在 SELECT 投影之后、LIMIT 之前,但排序准备(如载入 sort buffer)早在 WHERE 过滤后即开始;无 ORDER BY 时顺序不保证;覆盖索引可避免 filesort;ORDER BY 字段须出现在 SELECT 或 GROUP BY 中。

ORDER BY 是第10步,但排序动作实际发生在 SELECT 之后、LIMIT 之前
MySQL 的 ORDER BY 确实按语法顺序排在第10步,但它不是“简单地对最终结果排序”。真实执行中,排序是独立的计算阶段:server 层从存储引擎拿到满足 WHERE 条件的行后,就立刻送入 sort buffer 开始准备排序——此时 SELECT 列还没真正投影,DISTINCT 也还没去重。
这意味着:如果你 SELECT * 却只按某几个字段排序,MySQL 仍会把整行数据(包括大文本、BLOB)载入内存排序缓冲区,极易触发磁盘临时文件(Using filesort)。
为什么有时候没写 ORDER BY 却看起来有序?
这是常见错觉。MySQL 不保证无 ORDER BY 时的返回顺序,即使你看到的是“按主键顺序”:
- InnoDB 聚簇索引扫描可能碰巧返回有序数据,但一旦加了
JOIN、WHERE条件或查询优化器选了不同索引,顺序立刻打乱 - 并行查询、Buffer Pool 污染、版本升级都可能改变“看似稳定”的顺序
- 线上环境出现“昨天有序、今天乱序”,八成是执行计划变了,而不是 MySQL 抽风
文件排序(filesort)到底怎么工作的?
当 sort buffer 装不下所有待排序行时,MySQL 会启用磁盘归并排序:
- 先分批在内存中快排,每批生成一个
temp_file+ 对应的chunk_file(存起始偏移和记录数) - 多路归并最多同时读取 7 个
temp_file块,合并成更大块;最终合并成单个有序输出流 - 如果
ORDER BY字段有索引,且覆盖了查询所需列(即“覆盖索引”),可直接按索引 B+Tree 遍历,跳过 filesort
示例:表 orders(pay_time, status, amount),查询 SELECT amount FROM orders WHERE status='SUCCESS' ORDER BY pay_time —— 若有联合索引 (status, pay_time, amount),就能避免排序。
最容易被忽略的坑:HAVING 和 ORDER BY 的字段依赖关系
HAVING 过滤的是分组后的聚合结果,而 ORDER BY 默认只能引用 SELECT 列或分组字段。但很多人误以为能直接用聚合别名排序,其实:
- MySQL 5.7 允许
ORDER BY total_sales(别名),但这是语法糖,底层仍映射回SUM(payment_amount) - 若
SELECT中没包含该聚合字段(比如只写了SUM(x) AS s却在ORDER BY里用了未出现在SELECT中的y),会报错Unknown column 'y' in 'order clause' - 更隐蔽的问题:
GROUP BY a后ORDER BY b(b 非分组字段、非聚合结果),MySQL 8.0+ 默认拒绝,需开启sql_mode=ONLY_FULL_GROUP_BY关闭才允许(不推荐)
真正安全的做法是:确保 ORDER BY 的字段明确出现在 SELECT 列表中,或属于 GROUP BY 子句里的列。


















