order by 字段无索引会触发 filesort 导致性能骤降;应建合适单列或联合索引,遵循最左前缀原则,确保 where 等值字段在前、order by 字段紧随其后且方向一致,并优先覆盖查询字段以避免回表。

ORDER BY 字段没索引,查询直接变慢十倍
MySQL 或 PostgreSQL 里,ORDER BY 如果作用在无索引字段上,引擎大概率会触发 filesort——不是真排序文件,而是把数据全捞出来再内存/磁盘排序,IO 和 CPU 都扛不住。
实操建议:
- 用
EXPLAIN看执行计划,重点盯Extra列是否出现Using filesort - 给
ORDER BY的字段单独建索引,比如ORDER BY created_at DESC就建INDEX (created_at) - 复合查询带
WHERE又带ORDER BY时,优先建联合索引,顺序按「WHERE 等值字段 + ORDER BY 字段」排列,例如WHERE status = 'active' ORDER BY updated_at→ 建INDEX (status, updated_at) - 注意 ASC/DESC 在 MySQL 8.0+ 才支持混合方向索引;老版本建
INDEX (a DESC, b ASC)实际只当(a, b)处理
联合索引中 ORDER BY 字段位置错了,索引就废了
索引生效依赖最左前缀原则,ORDER BY 字段如果不在联合索引的连续后缀位置,优化器大概率弃用索引。
常见错误现象:
-
WHERE user_id = 123 AND category = 'book' ORDER BY score DESC,却建了INDEX (user_id, score, category)→category被跳过,score不连续,无法利用索引排序 - 建了
INDEX (a, b, c),但写ORDER BY b, c且没WHERE a = ?→ 索引失效
正确做法:
- 先确保
WHERE条件覆盖索引最左部分(等值匹配优先) -
ORDER BY字段必须紧接在WHERE等值字段之后,且方向一致(如都 ASC) - 如果
WHERE是范围查询(>,BETWEEN),它之后的字段只能用于过滤,不能用于排序 —— 比如INDEX (a, b, c)中WHERE a > 10 ORDER BY b,b无法走索引排序
SELECT * + ORDER BY 索引覆盖不足,回表开销大
即使 ORDER BY 走了索引,如果 SELECT 的字段不在索引里,数据库还得回到主键索引查完整行,叫「回表」。数据量一大,性能断崖下跌。
使用场景:分页列表、后台导出、聚合排序结果
- 用
EXPLAIN看key_len和Extra是否含Using index(说明索引覆盖,不用回表) - 把常用查询字段加进联合索引末尾,例如
SELECT id, title, score FROM posts WHERE status = 1 ORDER BY score DESC→ 建INDEX (status, score DESC, title, id) - 别盲目加太多字段进索引,索引体积变大,写入变慢,尤其是 TEXT/BLOB 类型字段尽量避免进索引
- PostgreSQL 对索引覆盖更敏感,没
INCLUDE子句的老版本,只能靠联合索引硬塞;新版本可用CREATE INDEX ON t (a, b) INCLUDE (c, d)把非排序字段附在索引末尾
ORDER BY NULL 和强制索引的取舍很危险
有人为了绕过 filesort,在查询末尾加 ORDER BY NULL,或用 FORCE INDEX 强制走某个索引。这在小数据量下看似快,上线后极易翻车。
-
ORDER BY NULL并不加速,只是告诉优化器“我不care顺序”,但如果业务逻辑依赖默认顺序(比如 InnoDB 主键顺序),结果可能错乱 -
FORCE INDEX会锁死执行路径,当数据分布变化(比如某字段高重复度)、统计信息过期,反而比优化器自动选的执行计划更差 - 真正该做的是删掉无用
ORDER BY—— 如果前端不需要排序,就别写;如果需要,就配对建索引,而不是堵漏洞
最容易被忽略的点:索引不是建完就一劳永逸。表数据量涨了 10 倍、查询条件变了、MySQL 升级到 8.0,都可能让原有索引失效。定期用 EXPLAIN 回看关键查询,比任何经验都管用。










