在MySQL中,ORDER BY和GROUP BY是常见的查询操作,但当它们无法利用索引有序性时,优化器会使用Filesort在内存或磁盘中排序,带来明显的性能损耗。通过设计合理的覆盖索引,可以让排序和分组直接基于索引完成,从而避免Filesort。

为什么ORDER BY和GROUP BY会产生Filesort
MySQL执行排序时,如果索引本身不能提供有序的数据,就会启用Filesort。GROUP BY在多数情况下也会被优化为排序操作。Filesort不是指一定在磁盘上排,但确实需要额外处理,消耗CPU和内存。
- 索引顺序与ORDER BY字段顺序不一致
- 排序字段不在同一个索引中
- 查询需要回表获取其他列,破坏覆盖性
覆盖索引如何避开Filesort
覆盖索引指查询所需的所有列都包含在索引中,MySQL只需读取索引即可返回结果。若索引列顺序与ORDER BY或GROUP BY一致,且为最左前缀匹配,排序就可由索引直接提供,不再触发Filesort。
设计联合索引的要点
假设有一张订单表,常按用户ID分组并按下单时间排序:
-- 建表语句 CREATE TABLE orders ( id INT PRIMARY KEY, user_id INT, created_at DATETIME, amount DECIMAL(10,2), INDEX idx_user_time (user_id, created_at) ); -- 可被覆盖索引优化的查询 SELECT user_id, created_at FROM orders WHERE user_id = 100 ORDER BY created_at; -- GROUP BY也可利用同索引 SELECT user_id, COUNT(*) FROM orders WHERE user_id = 100 GROUP BY user_id;
上面例子中,idx_user_time包含了过滤和排序字段,查询只取索引列,因此避免了Filesort。注意WHERE中的user_id是索引最左列,ORDER BY的created_at紧接其后,符合最左前缀原则。
验证是否使用了Filesort
使用EXPLAIN查看执行计划,若Extra列出现Using filesort说明发生了文件排序;若出现Using index则说明用了覆盖索引。
EXPLAIN SELECT user_id, created_at FROM orders WHERE user_id = 100 ORDER BY created_at;
| Extra值 | 含义 |
|---|---|
| Using index | 覆盖索引,无Filesort |
| Using filesort | 额外排序,需优化 |
| Using where; Using index | 索引过滤且覆盖 |
常见错误写法
下面写法会导致无法利用索引排序:
-- 错误:排序字段前跳过了索引最左列的范围条件 SELECT user_id, created_at FROM orders ORDER BY created_at; -- 错误:SELECT包含了非索引列,需回表 SELECT user_id, created_at, amount FROM orders WHERE user_id = 100 ORDER BY created_at;
在设计索引时,应优先把等值过滤字段放在联合索引前面,排序或分组字段放后面,并尽量只查询索引列。
总结
优化ORDER BY和GROUP BY的核心,是让索引既服务过滤又服务排序。通过构建顺序合理的覆盖索引,并收敛查询列,可以有效避开Filesort,显著提升MySQL查询效率。