order by 性能差主因是未合理使用索引或忽略排序底层行为;应优先让数据库走索引有序扫描避免filesort,满足最左前缀匹配、小数据集过滤或常量排序等条件时可免文件排序。

ORDER BY 性能差,通常不是因为 sql 写得“错”,而是没用对索引或忽略了排序的底层行为。核心原则是:让数据库尽量避免额外的文件排序(FileSort),优先走索引有序扫描。
哪些 ORDER BY 能直接走索引?
满足以下任一条件时,mysql 可以利用索引完成排序,无需 FileSort:
- ORDER BY 子句中的列,是某个可用索引的最左前缀,且顺序、方向(ASC/DESC)完全匹配(注意:5.7+ 支持混合方向,8.0+ 更灵活)
- WHERE 条件已过滤出极小数据集(如主键等值查询),即使排序列无索引,代价也低
- 排序字段全是常量(如 ORDER BY 1, ‘abc’),优化器直接忽略
例如:select * FROM orders WHERE user_id = 123 ORDER BY create_time DESC —— 若存在复合索引 (user_id, create_time),即可索引覆盖排序。
为什么加了索引还是慢?常见陷阱
索引存在 ≠ 排序走索引。这些情况会导致失效:
- ORDER BY 中混用 ASC 和 DESC(老版本 MySQL 不支持,会强制 FileSort)
- 排序字段参与了计算或函数(如 ORDER BY UPPER(name)、ORDER BY price*1.1)
- WHERE 和 ORDER BY 跨不同索引,无法共用(如 WHERE 用 idx_a,ORDER BY 用 idx_b)
- SELECT * 导致索引无法覆盖,回表后失去物理有序性,仍需二次排序
建议:用 EXPLAIN 查看 Extra 列,出现 using filesort 就说明触发了额外排序。
优化策略:从索引设计到查询改写
不只靠加索引,要结合场景综合调整:
- 优先创建联合索引,把高频过滤字段放前面,排序字段紧随其后(如 WHERE category=’A’ AND status=1 ORDER BY updated_at DESC → 索引 (category, status, updated_at))
- 避免 SELECT *,只查必需字段;若排序字段在索引中,可考虑覆盖索引减少回表
- 分页深翻(LIMIT 10000,20)性能陡降?改用“游标分页”(如 WHERE id > last_seen_id ORDER BY id LIMIT 20)
- 数据量极大且排序固定(如按时间倒序展示最新100条),可考虑冗余一个“排序序号”字段并建索引,或用物化视图预排序
ORDER BY + LIMIT 的协同效应
当带有 LIMIT 时,优化器可能提前终止排序。例如:
SELECT * FROM logs ORDER BY event_time DESC LIMIT 10
如果存在 event_time 索引,MySQL 可从索引末尾反向扫描10条,几乎不排序。但若加了 WHERE,必须确保 WHERE 条件也能命中同一索引,否则仍会全表扫描+排序。
注意:LIMIT 并不能“拯救”一个本该排序百万行的查询——它只在索引有序扫描路径成立时才高效。