ORDER BY 慢的关键在于触发 filesort,即无法利用索引排序;若索引能覆盖 ORDER BY 字段且顺序匹配,则可避免,EXPLAIN 中出现 Using filesort 即未走索引排序。

ORDER BY 为什么慢?关键在是否走索引
MySQL 的 ORDER BY 不一定慢,慢是因为它常触发 filesort —— 即无法利用索引完成排序,转而把数据读进内存或临时文件再排序。只要 ORDER BY 的字段和顺序能被某个索引“覆盖”,就大概率避免 filesort。
判断是否走索引,看 EXPLAIN 输出的 Extra 列:出现 Using filesort 就是没走排序索引;若只有 Using index 或为空,说明排序已下推到存储层。
- 复合索引顺序必须严格匹配
ORDER BY字段顺序(如索引是(a,b,c),则ORDER BY a,b可用,ORDER BY b,c不可用) - 升序/降序需一致:MySQL 8.0+ 支持混合方向索引(如
INDEX(a ASC, b DESC)),但 5.7 及以前只支持全 ASC 或全 DESC 索引用于排序 - WHERE 条件字段如果也在同一索引中,且满足最左前缀原则,该索引更可能被复用为排序索引
哪些 ORDER BY 场景必然触发 filesort
以下写法几乎无法利用索引排序,应主动规避:
-
ORDER BY RAND():每次都要打乱全表,无索引可依 -
ORDER BY ABS(x)、ORDER BY UPPER(name)等带函数的表达式:索引值 ≠ 计算后值,无法比对 -
ORDER BY a + b:同上,非确定性计算列无法走索引 - 跨表 JOIN 后
ORDER BY非驱动表字段:优化器通常放弃使用被驱动表的索引排序 -
SELECT *+ORDER BY+LIMIT大偏移量(如LIMIT 100000, 20):即使走了索引,也要跳过前 10 万行,I/O 和 CPU 成本高
如何为 ORDER BY 建高效索引
建索引不是给排序字段单独加一个,而是结合查询整体结构设计。核心原则是:让索引同时覆盖 WHERE 过滤 + ORDER BY 排序 + SELECT 返回字段(即“覆盖索引”)。
例如有查询:
SELECT id, name, created_at FROM users WHERE status = 1 ORDER BY created_at DESC LIMIT 20;
iWebShop是一款基于PHP语言及MYSQL数据库开发的B2B2C多用户开源免费的商城系统,系统支持自营和多商家入驻、集成微信商城、手机商城、移动端APP商城、三级分销、视频电商直播、微信小程序等于一体,它可以承载大数据量且性能优良,还可以跨平台,界面美观功能丰富是电商建站首选源码。iWebShop开源商城系统 v5.14 更新日志:新增商品编辑页面规格图片上传优化商品详情页面规格图片与主图切
最优索引是:
ALTER TABLE users ADD INDEX idx_status_created (status, created_at DESC);
- 把等值条件字段
status放最左(满足最左前缀) - 紧跟排序字段
created_at,并显式声明DESC(MySQL 8.0+ 支持;5.7 可省略,但实际仍按索引定义方向扫描) - 若还频繁查
name,且不想回表,可扩展为(status, created_at DESC, name)—— 但注意索引宽度增加,写入开销上升 - 避免在该索引中加入
id:主键自动包含在二级索引中,无需重复
ORDER BY 性能陷阱:LIMIT 配合偏移量的隐性成本
LIMIT m,n 在 MySQL 中本质是“先取 m+n 行,再丢弃前 m 行”。当 m 很大(比如分页到第 1000 页),即使走了索引,也要遍历大量索引节点。
替代方案不是换写法,而是换思路:
- 用游标分页(cursor-based pagination):记录上一页最后一条的
created_at值,下一页查WHERE created_at - 对高频分页场景,预生成分页映射表(如记录每页首条主键 ID),用主键查而非偏移量
- 禁止前端传任意大的
OFFSET,后端强制截断(如最大只允许OFFSET 5000)
真正难优化的不是 ORDER BY 本身,而是它和业务逻辑耦合后产生的“假需求”——比如要求用户能翻到第 10 万页的评论。这种场景,索引再好也救不了设计缺陷。










