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维护一个大索引。
