ORDER BY 字段必须是索引的最左前缀
MySQL 只有在
ORDER BY子句匹配索引的**连续最左列**时,才能避免文件排序(
Using filesort)。比如有联合索引
INDEX (a, b, c),以下写法能走索引:
ORDER BY a
ORDER BY a, b
ORDER BY a, b, c
但
ORDER BY b、
ORDER BY a, c(跳过
b)、
ORDER BY b, c都无法利用该索引做排序。注意:即使
a有
=条件,
ORDER BY b, c仍大概率触发
Using filesort—— 因为索引中
a值相等的记录,
b, c部分才是有序的,而优化器通常不认为这足够“全局有序”。
避免混合 ASC/DESC 导致索引失效
MySQL 8.0 之前,如果联合索引定义为
INDEX (a ASC, b ASC),而查询写成
ORDER BY a ASC, b DESC,就无法使用该索引排序(会回退到
Using filesort)。MySQL 8.0+ 支持降序索引,可显式建为
INDEX (a ASC, b DESC),此时
ORDER BY a ASC, b DESC才能命中。 检查执行计划:出现
Using filesort就说明没走索引排序 旧版本想兼容混合方向,只能建两个索引(如
(a,b)和
(a,b DESC)),但后者在 5.7 及以前语法不合法
DESC在索引定义中不是注释,是真实存储方向,影响 B+ 树遍历顺序
WHERE + ORDER BY 组合时,索引要覆盖过滤和排序需求
典型陷阱:给
WHERE status = 1建了
INDEX (status),又想
ORDER BY created_at,结果还是
Using filesort—— 因为单列索引
status不含
created_at,无法排序。 应建联合索引:
INDEX (status, created_at)(注意顺序:先 where 列,再 order by 列) 如果还有
SELECT *或需要返回其他字段,考虑是否加覆盖索引,避免回表(例如
INDEX (status, created_at, id, name)) 当
WHERE条件用的是范围(如
status > 0),则
ORDER BY后续列**无法使用索引排序**(B+ 树中范围扫描后的数据不再保证有序)
小结果集别强求索引排序,大分页慎用 LIMIT offset
对
ORDER BY id LIMIT 10这类小结果,即使没走索引排序,成本也极低;但
ORDER BY created_at LIMIT 10000, 20会让 MySQL 先排序前 10020 行,再丢弃前 10000 行 —— 即使
created_at有索引,I/O 和 CPU 开销依然巨大。 改用游标分页:
WHERE created_at如果必须跳转,考虑用延迟关联(
SELECT * FROM t JOIN (SELECT id FROM t ORDER BY x LIMIT 10000,20) AS tmp USING(id))减少排序行数
SQL_BUFFER_RESULT有时能缓解,但治标不治本
真正难优化的,往往不是“有没有索引”,而是“索引列顺序是否贴合查询模式”以及“范围条件之后还能不能排序”。这两个点漏掉一个,
EXPLAIN里就稳稳写着
Using filesort。
