order by字段未走索引是触发using filesort的最常见根因;必须确保其顺序、方向与复合索引严格一致,where条件命中索引最左前缀,避免函数、join后排序及大偏移分页。

ORDER BY字段没走索引,直接触发filesort
这是最常见也最容易被忽略的根因。MySQL/PostgreSQL看到ORDER BY字段不在可用索引上,就会强制走Using filesort——哪怕只查10条,也可能先排序百万行再取前N条。
关键判断依据是EXPLAIN输出里出现Using filesort或Using temporary。此时不是数据量大,而是排序路径错了。
- 确保
ORDER BY字段顺序与复合索引列顺序严格一致(如ORDER BY a DESC, b ASC需对应INDEX(a DESC, b ASC);MySQL 8.0+支持混合方向,旧版本必须全同向) - WHERE条件要能命中该索引前缀:比如
WHERE status = 'paid' ORDER BY created_at,索引就得是(status, created_at),而不是单列created_at - 避免在
ORDER BY字段上用函数:ORDER BY UPPER(name)会失效;需要大小写不敏感时,改用函数索引或生成列
JOIN后才排序,中间结果集爆炸
多表JOIN生成的临时结果远大于最终要排序的行数,数据库被迫在内存或磁盘里对几十万行做排序——这比单表排序慢一个数量级。
典型现象:EXPLAIN中rows值远高于你预期的返回行数(比如JOIN输出50万行,但LIMIT 20只取20条)。
- 把排序下推到驱动表:如果只按
users.name排序,优先查users表带LIMIT,再用IN或JOIN补关联字段 - 用覆盖索引减少回表:例如
SELECT id, title FROM posts ORDER BY publish_time DESC,建索引(publish_time, id, title) - 确认JOIN类型是否必要:LEFT JOIN保留左表全量,常导致中间集膨胀;若业务实际只要匹配行,直接换
INNER JOIN
深分页(OFFSET过大)让ORDER BY彻底失速
LIMIT 10000, 20这类查询,数据库仍得先排序全部匹配行,再跳过前10000条——索引形同虚设。
这不是SQL写法问题,而是分页模型本身缺陷。即使加了索引,OFFSET越大,越接近全表扫描。
- 改用游标分页:记录上一页最后一条的
created_at和id,下一页查WHERE created_at - 避免
SELECT *参与排序:只选必要字段,尤其别在JOIN后SELECT *再ORDER BY,回表+排序双重开销 - 对高频分页场景,考虑物化排序视图或定时预计算(如每日凌晨刷出Top 1000热门商品ID列表)
JOIN顺序和索引缺失让优化器放弃索引排序
优化器发现某张表缺关键索引,就可能放弃重排JOIN顺序,硬按SQL字面顺序执行——小表在后、大表在前,中间结果集直接翻倍,后续ORDER BY根本没机会走索引。
特别容易发生在LEFT JOIN右表的ON字段没索引,或驱动表过滤条件没覆盖索引时。
- 被驱动表的
ON字段必须单独建索引:比如orders.user_id不能只依赖(user_id, created_at),除非查询也用到created_at - 驱动表的WHERE字段+ORDER BY字段一起建联合索引:如
SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id WHERE u.status = 'active' ORDER BY o.created_at,优先建users(status, id)和orders(user_id, created_at) - 用
STRAIGHT_JOIN(MySQL)或LATERAL子查询(PostgreSQL)人工控制顺序,但仅当EXPLAIN FORMAT=TREE确认优化器选错路径时才用











