ORDER BY 触发 filesort 是因无法利用索引有序性而启用额外排序;常见原因包括排序字段不在索引最左前缀、ASC/DESC 混用、函数表达式排序、非驱动表字段排序等,EXPLAIN 中出现 Using filesort 即表明该情况。

为什么 ORDER BY 会触发 filesort?
MySQL 在无法利用索引完成排序时,就会回表后做内存或磁盘排序,也就是 filesort。这不是“文件写入硬盘”的字面意思,而是优化器放弃索引有序性、改用额外排序步骤的标志。
常见诱因包括:
-
ORDER BY字段不在索引最左前缀上(比如索引是(a, b),却按b排序) - 混合 ASC/DESC:索引是
(a ASC, b ASC),但查询写成ORDER BY a ASC, b DESC(8.0.12+ 支持反向索引扫描,但需显式定义) - 对函数或表达式排序:
ORDER BY UPPER(name)、ORDER BY a + b,索引失效 - 连接查询中,
ORDER BY字段来自非驱动表,且无合适覆盖索引
怎么一眼看出是不是 filesort?
看 EXPLAIN 的 Extra 列。只要出现 Using filesort,就说明排序没走索引。
注意两个易混淆点:
-
Using temporary+Using filesort:通常出现在含GROUP BY和ORDER BY且字段不一致时,临时表本身也会触发排序 -
Using index不代表没 filesort——它只说明用了覆盖索引取数据,排序仍可能独立发生
实操建议:在慢查询日志里加 log_queries_not_using_indexes = ON,但别长期开着,它会误报简单主键查询。
加索引就能解决 filesort 吗?
不一定。索引设计必须严格匹配查询模式,否则只是“看起来有索引”。
关键原则:
- WHERE 条件字段 + ORDER BY 字段,要能构成同一索引的最左前缀;例如
WHERE status = ? AND category = ? ORDER BY created_at,索引应建为(status, category, created_at) - 如果还有
LIMIT,且数据量大,优先保证排序字段在索引末尾——MySQL 可以用索引直接跳到第 N 行,避免全排序 - 避免冗余索引:已有
(a, b, c),再建(a, b)对排序无额外帮助,还拖慢写入
示例:查询 SELECT * FROM orders WHERE user_id = 123 ORDER BY pay_time DESC LIMIT 10,建索引 (user_id, pay_time) 即可,不必包含其他字段——除非你想覆盖 SELECT。
排序缓冲区 sort_buffer_size 调多大才有效?
它只控制单次排序能用多少内存,不是越大越好。
典型陷阱:
- 设太大(如 256M),会导致并发高时内存耗尽,触发 swap 或 OOM killer
- 设太小(默认 256K),频繁落盘,反而更慢;但调到 4M–8M 通常是安全边际
- 这个值是 per-connection 的,总内存占用 = 连接数 ×
sort_buffer_size
真正影响性能的是是否能进内存排序。可通过 SHOW STATUS LIKE 'Sort%' 观察:Sort_merge_passes 高,说明经常归并排序,该调了;Sort_scan 高,说明很多查询没走索引排序。
复杂点在于:有些查询即使加了索引,MySQL 也可能因统计信息不准或成本估算偏差,主动放弃索引排序。这时候需要 FORCE INDEX 或更新统计信息(ANALYZE TABLE)。


















