首页/数据/SQL 速查/消除排序步骤的常见手法
性能与执行计划72 条

消除排序步骤的常见手法

让 ORDER BY 能按索引顺序取数:列序列、方向、条件都要匹配;否则数据库要收集后排序。

SQL 速查数据更新于 2026-10-13

让排序直接用索引顺序

排序本身不慢,慢的是为排序把大量行收集起来再排。如果能按索引已有的顺序取数,排序步骤就可以完全去掉。条件是把列的顺序、方向、以及过滤条件这三者对齐:等值过滤的列放在索引前面,排序的列紧跟其后,排序方向与索引方向一致。做不到对齐时,就要想办法减少参与排序的行数。

基本信息

适用平台MySQL 8.0 / PostgreSQL 12 及以上
对齐要点过滤列在前导、排序列紧随其后、方向一致
方向处理反向扫描是允许的,混合方向才需要额外处理
表达式排序排序键是表达式时普通索引无法直接利用
结果判读计划里出现排序提示说明排序在内存或磁盘上单独完成

典型使用场景

  • 列表页按时间倒序分页:希望直接取索引尾部
  • 按用户维度排序取最近记录:判断能否消除排序
  • 排查排序落盘:大结果集排序溢出到磁盘
  • 表达式排序优化:考虑改为函数索引或调整存储
  • 评估是否需要为排序单独建索引

可消除与不可消除的排序

可消除与不可消除的排序
-- 建立与排序匹配的复合索引
ALTER TABLE orders ADD INDEX idx_user_created (user_id, created_at);

-- 可消除排序:等值列在前导,排序列紧随其后
EXPLAIN SELECT * FROM orders
WHERE user_id = 100
ORDER BY created_at DESC
LIMIT 20;

-- 需要额外排序:排序键与索引顺序不匹配
EXPLAIN SELECT * FROM orders
WHERE user_id = 100
ORDER BY total DESC
LIMIT 20;      -- 计划里会出现排序提示或独立排序节点

-- 排序无法消除时,先把参与排序的行数压下来
-- 用条件缩小结果集,或在子查询里先筛选再排序

-- 表达式排序需要函数索引或把计算列落库
-- ORDER BY LOWER(name) 这类写法无法直接利用普通索引

能否消除排序

查询形态能否利用索引顺序说明
等值过滤加排序列可以索引前导等值,后面接排序列
范围过滤加排序列多数情况不行范围之后的有序性被打断
多列排序方向不一致不行混合方向需要额外处理
按表达式排序不行需要函数索引或计算列
排序键与索引顺序颠倒不行列顺序不对齐则无法复用
出现排序步骤不等于性能问题:结果集只有几十行时,内存里排一次远比强行走索引再回表更快。判断依据应该是实际耗时与扫描行数,而不是计划里有没有排序字样。
为了消除排序而改排序键风险很大:排序顺序本身就是业务语义,改了会返回错误顺序的数据。索引调整应该服务于原有排序,而不是反过来改变查询行为。

易错点

  • 只看计划里有没有排序,不看代价:小结果集排序本来就更划算,强行优化反而更慢
  • 为了消除排序改变排序键:业务语义被改掉,返回顺序错误且难以察觉
  • 范围过滤后接排序列,误以为能利用索引顺序,实际有序性已被打断
  • 用表达式排序却没建函数索引,每次都全量排序
  • 为排序单独建索引却不检查写入影响:索引维护成本持续增加

数据来源与参考

  • MySQL 8.0 Reference Manual · ORDER BY Optimization
  • PostgreSQL Documentation §11.4 Indexes and ORDER BY
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌