order by未走索引的典型表现是explain显示type为all或index且extra含using filesort;应建联合索引使排序字段为最右连续部分并方向一致,避免表达式排序、混合升降序及索引跳过。

ORDER BY 没走索引的典型表现
执行 EXPLAIN 后发现 type 是 ALL 或 index,且 Extra 列出现 Using filesort,基本可以断定排序没走索引。这不是警告,是明确的性能红灯——MySQL 正在把结果集全捞出来再内存或磁盘排序。
常见诱因包括:
-
ORDER BY字段未建索引,或索引顺序与查询条件+排序字段不匹配 - 对非前缀字段排序,比如联合索引
(a, b, c),却写ORDER BY b - 混合 ASC/DESC(如
ORDER BY a ASC, b DESC),而索引定义是(a, b)全 ASC - 对函数或表达式排序,例如
ORDER BY UPPER(name)
如何让 ORDER BY 走索引
核心原则:让 ORDER BY 字段成为索引的最右连续部分,且方向一致。
实操建议:
- 若常查
WHERE status = ? ORDER BY created_at,建联合索引(status, created_at),不是单列索引created_at - 若需
ORDER BY a ASC, b DESC,MySQL 8.0+ 支持降序索引,可建(a ASC, b DESC);5.7 及以前只能统一 ASC,此时需评估是否接受应用层二次排序 - 避免在
ORDER BY中使用CASE、CONCAT等表达式;真有需要,考虑生成列 + 索引(MySQL 5.7+) - 检查
WHERE条件是否“截断”了索引:比如索引是(a, b, c),但查询写了WHERE a = ? AND c = ?(跳过b),则ORDER BY c无法利用该索引
LIMIT 配合 ORDER BY 的隐藏陷阱
ORDER BY ... LIMIT N 看似能减少数据量,但 MySQL 仍可能扫描大量行才凑够 N 条满足条件的结果——尤其当 WHERE 过滤率低时。
优化方向:
- 确保
WHERE + ORDER BY能命中同一联合索引,让排序和过滤都在索引内完成 - 避免
LIMIT偏移过大,如LIMIT 10000, 20;改用游标分页(记录上一页最大id或created_at值) - 如果业务允许,加
FORCE INDEX强制走排序索引,防止优化器误选全表扫描(但需测试验证) - 注意
SQL_CALC_FOUND_ROWS已被弃用,分页总数应单独用COUNT(*)估算或缓存
排序缓冲区(sort_buffer_size)调优要点
当无法避免 Using filesort 时,至少让它在内存里完成。默认 sort_buffer_size 通常太小(256KB),大结果集会频繁落盘,性能断崖式下跌。
调整建议:
- 该参数是**每个连接独享**的,不能设得过大(比如 128MB),否则并发高时内存爆炸
- 观察
SHOW STATUS LIKE 'Sort_merge_passes':值持续上升说明频繁归并排序,需适当调大sort_buffer_size - 优先保证
tmp_table_size和max_heap_table_size≥sort_buffer_size,避免临时表被迫转磁盘 - 线上调参后务必压测,不同查询负载下最优值差异很大
真正难的不是加索引,而是识别哪些 ORDER BY 实际上根本不需要——比如列表页默认按时间倒序,但用户从不翻到第 10 页以后,那 LIMIT 20 就已足够,不必为 ORDER BY id DESC 维护一个大索引。











