ORDER BY 变慢主因是未走索引导致 Using filesort,需按 WHERE+ORDER BY 字段顺序创建复合索引,并避免函数排序、跨列计算等无法利用索引的场景。

为什么 ORDER BY 一加就变慢?先看执行计划有没有用上索引
MySQL 对 ORDER BY 的优化依赖于是否能复用已有的索引,而不是“有索引就行”。常见现象是:加了 WHERE 条件查得快,但一加 ORDER BY 就几十秒——大概率是执行计划里出现了 Using filesort。
执行 EXPLAIN SELECT ... ORDER BY ...,重点看 type(是否为 range/ref)、key(是否命中预期索引)、Extra(是否含 Using filesort)。如果 key 是 NULL 或 Extra 出现 Using filesort,说明排序没走索引,数据量大时必然慢。
- 复合索引字段顺序必须匹配查询模式:
WHERE条件字段在前,ORDER BY字段紧接其后 - 如果
WHERE a = ? AND b > ?,再ORDER BY c,那索引应为(a, b, c),不是(a, c, b) -
ORDER BY中混用ASC和DESC(如ORDER BY x ASC, y DESC)在 MySQL 8.0 之前无法用单个索引覆盖,8.0+ 支持,但需显式声明INDEX (x ASC, y DESC)
WHERE + ORDER BY 共用索引时,字段顺序怎么排才不踩坑
索引最左前缀原则不是“只要开头匹配就行”,而是“连续匹配且满足排序需求”。例如查询 SELECT * FROM t WHERE status = 1 AND created_at > '2024-01-01' ORDER BY updated_at DESC:
- 建
(status, created_at, updated_at):可走索引过滤 + 排序,Extra不出现Using filesort - 建
(status, updated_at, created_at):created_at > ?是范围查询,索引在updated_at处就断了,ORDER BY updated_at无法利用后续字段,仍会Using filesort - 如果还有
LIMIT 20,且数据分布倾斜(比如大部分status = 1的记录集中在旧时间),即使有索引,也可能因回表过多或扫描行数超预期而变慢
哪些 ORDER BY 场景注定没法走索引?提前识别避免白忙
不是所有排序都能靠加索引解决。以下情况即使建了索引,MySQL 也大概率放弃使用:
- 对函数或表达式排序:
ORDER BY UPPER(name)、ORDER BY a + b—— 索引存的是原始值,无法直接比较 - 跨列计算:
ORDER BY ABS(score)、ORDER BY CONCAT(first_name, last_name) - 涉及不同字符集或排序规则的列混合排序(如
utf8mb4_0900_as_csvsutf8mb4_general_ci),触发隐式转换导致索引失效 -
ORDER BY字段和SELECT字段不在同一张表(如多表 JOIN 后排序),尤其当驱动表选择不当,优化器可能放弃索引下推
这类场景要么改写查询(如增加生成列并建索引),要么接受排序走临时文件,再通过控制 sort_buffer_size 和 tmp_table_size 缓解内存压力。
验证索引是否真生效:别只信 EXPLAIN,要看 Handler_read_* 和实际扫描行数
EXPLAIN 显示用了某个索引,不代表它真的高效。真正关键的是执行时到底读了多少索引页、多少数据页。
- 执行前先
FLUSH STATUS,再运行查询,然后查SHOW STATUS LIKE 'Handler_read%' - 重点关注
Handler_read_next(索引遍历次数)和Handler_read_rnd_next(回表读取行的次数):如果后者远大于前者,说明索引覆盖差,大量随机 I/O - 对比
rows_examined(实际扫描行数)和结果集rows_sent:若比值超过 10 倍,说明索引选择性差或条件过滤弱,要考虑调整索引字段顺序或补充过滤条件 - 用
SELECT ... INTO DUMPFILE或慢日志里的Rows_examined字段做交叉验证,避免开发环境数据量小带来的误判
索引顺序不是调参,是数据访问路径的设计。一个字段放错位置,整个排序逻辑就退化成全表扫描;而一次 Handler_read_rnd_next 暴涨,往往意味着磁盘随机读成了瓶颈,这时候再加内存也没用。


















