order by用不上索引的最常见原因是字段无索引或索引顺序不匹配最左前缀原则,如where city = ? order by age时仅建city单列索引,或联合索引为(age, city)但city非最左列,均导致using filesort。

ORDER BY字段没索引或索引顺序不匹配
最常见原因就是ORDER BY用的列压根没建索引,或者虽然有索引但结构不适用。比如查询写的是 SELECT * FROM users WHERE city = 'Shanghai' ORDER BY age DESC,但只在 city 上建了单列索引——数据库无法利用它完成排序,只能全表扫描后做 Using filesort。
联合索引必须严格遵循「等值条件在前、排序字段紧随其后」的顺序。如果建的是 (age, city),而查询条件是 WHERE city = ?,那这个索引对 ORDER BY age 也无效——city 不是最左前缀,索引直接被跳过。
- MySQL 8.0+ 支持混合方向索引(如
(city ASC, age DESC)),但旧版本要求全部升序才可能复用 -
ORDER BY a DESC, b DESC可以走(a, b)索引;但ORDER BY a DESC, b ASC在 MySQL 5.7 就大概率失效 - 索引列含
NULL值时,该列在 B+ 树中可能不参与排序路径,尤其当WHERE条件涉及IS NULL时,优化器常主动放弃索引
查询字段太多触发回表,让索引“形同虚设”
即使 ORDER BY 走了索引,如果 SELECT * 或包含大量非索引列,InnoDB 还得根据主键一个个回表取数据。这时 type = index 看似走了索引,实际是遍历整棵索引树,性能和全表扫描接近。
EXPLAIN 中看到 Extra 是 Using index 才算真·覆盖;若出现 Using where; Using index,说明索引只用于过滤/排序,但数据还得回查聚簇索引。
- 把常用查询字段加进联合索引末尾,构成覆盖索引,例如
(city, age, name, email) - 避免
SELECT *,只选真正需要的列,减少 I/O 和内存压力 - 大文本字段(
TEXT、BLOB)会强制回表,即使其他字段都在索引里
WHERE + ORDER BY 组合导致索引“半失效”
当 WHERE 条件用了范围查询(>、、<code>BETWEEN、LIKE 'abc%'),MySQL 只能用索引中该列及之前的部分,后面的列无法用于排序。
例如索引是 (a, b, c),查询 WHERE a = 1 AND b > 10 ORDER BY c:前缀 a 匹配,b > 10 是范围,所以 c 无法被索引排序——仍会 Using filesort。
- 把排序字段提前到范围条件之前,比如改用
(a, c, b)索引(前提是c也能用于过滤) - 如果业务允许,把范围条件改成等值(如枚举状态码),就能释放后续排序字段
-
LIKE开头带通配符('%xxx')会让整个索引失效,不只是排序部分
sort_buffer_size 太小,被迫写磁盘排序
即使索引可用,如果结果集太大,而 sort_buffer_size 不够,MySQL 仍会把中间结果写入磁盘临时文件,I/O 开销陡增。这时候 EXPLAIN 看不到 Using filesort,但查询就是卡。
可通过 SHOW STATUS LIKE 'Sort_merge_passes'; 监控:数值持续上升,基本就是磁盘排序频繁发生。
- 该参数是每个连接独占内存,设太高会撑爆物理内存,建议从 2M → 4M → 8M 逐步调,观察效果与内存使用
- 更治本的办法是缩小结果集:加
LIMIT、强化WHERE过滤、或改用游标分页替代OFFSET - 注意
tmpdir所在磁盘是否为 SSD,机械盘上磁盘排序延迟可能高达百毫秒级
真正卡顿往往不是单一原因。比如你看到 Using filesort,第一反应是加索引;但如果 WHERE 条件本身已触发全表扫描,那索引再好也白搭——得先看 type 是不是 ALL 或 index。











