using filesort 不代表排序失败,仅说明 mysql 无法利用索引顺序直接返回结果而需额外排序;其性能取决于是否在内存中完成(sort_buffer够用)还是落盘(number_of_tmp_files > 0)。

EXPLAIN里出现 Using filesort 就代表排序失败了吗
不是。Using filesort只是说明 MySQL 没法直接用索引顺序返回结果,必须额外做一次排序——它不等于“慢”,但大概率意味着没走最优路径。真正的性能分水岭在于:这个排序是纯内存快速排序(sort_buffer够用),还是被迫写磁盘临时文件(number_of_tmp_files > 0)。前者可能毫秒级,后者 I/O 一上来,响应就不可控。
为什么有索引还触发 filesort
常见错觉是“只要 ORDER BY 字段有索引,就一定不 filesort”。实际失效场景非常多:
-
SELECT *+ 主键索引:主键索引能定位行,但SELECT *要回表取所有列,回表过程打乱了主键物理顺序,MySQL 只能先把数据捞出来再排 - 联合索引顺序不匹配:比如索引是
(status, created_at),但查询写ORDER BY created_at—— 缺少最左列status的 WHERE 条件,索引无法按created_at有序扫描 - 排序方向混用:索引定义为
(a ASC, b ASC),但查询写ORDER BY a ASC, b DESC,MySQL 8.0 之前完全不支持,必然退化 - 对字段加函数:如
ORDER BY UPPER(name)或ORDER BY DATE(created_at),索引值被计算破坏,无法直接比对
如何确认是不是覆盖索引问题
核心判断依据是 EXPLAIN 输出中 key 和 Extra 的组合:
- 如果
key显示用了某个索引(比如PRIMARY或idx_status_created),但Extra仍有Using filesort→ 大概率是字段没覆盖,需要回表 - 把
SELECT *改成只查索引里有的字段,例如索引是(status, id, name),就写SELECT id, name FROM t WHERE status = 1 ORDER BY id - 建覆盖索引时,务必把
WHERE条件列放在前面,ORDER BY列紧随其后,且方向一致;避免中间跳列或方向不统一
LIMIT 偏移量大时 filesort 更要命
ORDER BY id LIMIT 10000, 20 看似只取 20 行,但 MySQL 必须先找到前 10000 行满足条件的记录——哪怕 id 有索引,也要“数够”才能开始返回。这导致:
- sort_buffer 不是只存 20 行,而是要容纳至少 10020 行(甚至更多,取决于筛选比例)
- 一旦超出
sort_buffer_size,立刻落盘,产生多个临时文件,归并成本陡增 - 在 RR 隔离级别下,这个长扫描过程会延长一致性读快照范围,锁等待风险同步上升
真正卡住你的,往往不是排序本身,而是“不得不扫那么多行”这个前提。优化方向不是调大 buffer,而是改成分页游标(如 WHERE id > last_seen_id ORDER BY id LIMIT 20)。











