ORDER BY 走索引需满足最左前缀匹配、方向一致且未被WHERE中断;EXPLAIN中Extra无Using filesort且type为range/index即表示走索引,否则触发内存或磁盘排序,性能风险高。

能走索引就别让 MySQL 自己排序——这是优化 ORDER BY 最根本的判断。只要执行计划里出现 Using filesort,说明排序已脱离索引控制,性能风险立刻上升。
怎么判断 ORDER BY 是否走了索引?
用 EXPLAIN 看 Extra 列:
- 没出现
Using filesort,且Type是index或range,大概率走索引排序 - 出现
Using filesort,说明 MySQL 正在内存或磁盘里做额外排序 - 同时出现
Using temporary+Using filesort,问题更严重,意味着先建临时表再排序
注意:索引是否生效不看有没有建,而看是否满足最左前缀+方向一致+未被 WHERE 中断。比如索引是 (status, create_time),WHERE status = 1 ORDER BY create_time DESC 可以用;但 WHERE status > 0 ORDER BY create_time 就不行——因为范围查询后,create_time 在索引中已无序。
哪些写法会让索引失效?
常见但隐蔽的“自废武功”操作:
- 在排序字段上用函数:
ORDER BY UPPER(name)、ORDER BY DATE(created_at),强制逐行计算,索引完全失效 - 混合排序方向:
INDEX(a ASC, b ASC)无法支持ORDER BY a ASC, b DESC(MySQL 8.0+ 需显式定义INDEX(a ASC, b DESC)) - 跳过前导列:
INDEX(a, b, c)无法支持ORDER BY b, c,必须从a开始 - SELECT * 配合 LIMIT:即使有索引,也要回表读全行,大 OFFSET 下 I/O 放大数倍
真正有效的优化手段有哪些?
不是堆参数,而是控制数据流和访问路径:
- 建覆盖索引:把
WHERE条件列、ORDER BY列、SELECT字段全包进一个复合索引,例如CREATE INDEX idx_status_ctime_id ON t(status, create_time DESC, id),这样SELECT id FROM t WHERE status=1 ORDER BY create_time DESC LIMIT 20就能全程走索引,零回表、零 filesort - 用游标分页替代 OFFSET:把上一页最后一条的
create_time和id当作下一页条件,如WHERE create_time ,避免扫前 N 行 - 调小
sort_buffer_size全局值,改用会话级设置:比如SET sort_buffer_size = 4194304(4MB),既防内存爆炸,又避免因默认 256KB 太小导致频繁磁盘归并(Sort_merge_passes升高)
复杂点在于:索引设计必须贴合实际查询模式,而不是凭感觉堆字段;而 sort_buffer_size 的合理值取决于单次排序的数据量和并发连接数,设太大反而拖垮整体内存。这两者都得靠 EXPLAIN 和 SHOW GLOBAL STATUS LIKE 'Sort%' 实测反馈,没法一劳永逸。



















