驱动表上建组合索引能绕过Using filesort,因为只有驱动表的索引才能提供JOIN扫描时的天然排序顺序;单列索引无法满足多字段ORDER BY的联合有序要求,必须按WHERE字段在前、ORDER BY字段在后的顺序创建组合索引。

为什么驱动表上建组合索引能绕过 Using filesort
因为 MySQL 在执行 JOIN 时,如果驱动表的扫描顺序天然符合 ORDER BY 字段的排序要求,优化器就能直接按序读取数据,无需事后排序。而这个“天然顺序”只能靠索引提供——单字段索引只保证一个字段有序,ORDER BY a, b 却需要两个字段联合有序,所以必须用组合索引。
EXPLAIN 看到 Using filesort 时,先确认驱动表是谁
执行 EXPLAIN 后,看 table 列第一行对应的表——那就是驱动表。它决定了整个 JOIN 的扫描起点和数据流方向。常见错误是只给被驱动表(比如 orders)建了 (user_id, created_at),但驱动表是 users,ORDER BY orders.created_at 依然无法利用该索引。
- 如果驱动表是
users,且要按orders.created_at排序,那基本无解——得换写法(如子查询或LATERAL) - 如果驱动表是
orders,且ORDER BY created_at DESC,那就给orders建INDEX (created_at DESC)或更优的INDEX (user_id, created_at DESC) - MySQL 8.0+ 支持降序索引,但 5.7 不支持,建
DESC会自动转为ASC,导致排序方向不匹配,仍触发filesort
组合索引字段顺序不能错:WHERE 条件字段必须在前,ORDER BY 字段紧随其后
例如查询:SELECT u.name, o.amount FROM orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'paid' ORDER BY o.created_at DESC。这里 orders 是驱动表,status 是过滤条件,created_at 是排序字段——索引必须是 INDEX (status, created_at DESC),而不是 (created_at, status)。
-
(status, created_at)能同时满足WHERE和ORDER BY,扫描时按status分组、再按created_at排序,自然有序 -
(created_at, status)只能加速排序,但WHERE status = 'paid'会变成索引范围扫描中的“跳查”,失去顺序性 - 若还有
SELECT中的非索引字段(如o.amount),可扩展为覆盖索引:INDEX (status, created_at DESC, amount),避免回表
别忽略 LIMIT 对索引选择的影响
当语句带 LIMIT(尤其是分页偏移大时),优化器可能放弃走排序索引,转而选更快返回前几行的执行路径。比如 LIMIT 10000, 20,即使有 (created_at) 索引,也可能先全扫再排序再截断。
- 用游标分页替代
OFFSET:记录上一页最后的created_at和id,下一页查WHERE created_at - 强制走索引:
SELECT ... FROM orders FORCE INDEX (idx_status_created) JOIN ...,但需配合EXPLAIN验证是否真生效 - 注意:复合索引中
DESC定义在高频写入表上可能加剧页分裂,上线前要在压测环境验证写入性能
ORDER BY 字段所在表建索引,却没看 EXPLAIN 第一行,结果索引建了也白建。


















