SQL 索引与 ORDER BY 优化技巧

2次阅读

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

SQL 索引与 ORDER BY 优化技巧

ORDER BY 字段没索引,查询直接变慢十倍

mysqlpostgresql 里,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 bb 无法走索引排序

select * + ORDER BY 索引覆盖不足,回表开销大

即使 ORDER BY 走了索引,如果 SELECT 的字段不在索引里,数据库还得回到主键索引查完整行,叫「回表」。数据量一大,性能断崖下跌。

使用场景:分页列表、后台导出、聚合排序结果

  • EXPLAINkey_lenExtra 是否含 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 回看关键查询,比任何经验都管用。

text=ZqhQzanResources