ORDER BY 与索引是什么
排序是昂贵操作:当排序列与条件列同属一个索引且顺序吻合时,B+ 树本身就是有序的,可以直接顺序扫描而不额外排序;否则就要取出全部结果再排(多出排序步骤)。升降序混用需要 8.0 的降序索引支持,低版本只能靠调整索引或改写语句。
基本信息
| 基本规则 | 等值条件列在前,排序列紧随其后,顺序要一致 |
|---|---|
| 判断依据 | 执行计划附加信息里是否出现排序标记 |
| 降序索引 | MySQL 8.0 支持真正的降序索引定义 |
| 临界值 | LIMIT 很小时优化器可能主动选择排序,未必是坏事 |
| 覆盖索引 | 索引包含查询所需列时可避免回表,效果更佳 |
典型使用场景
- 列表页按时间倒序:用户维度的最新记录查询,是最常见的场景
- 分页查询:深翻页时排序开销被放大,索引收益更明显
- 排行榜与统计:按分数或金额排序并取前若干条
- 范围条件加排序:范围列之后接排序列,只能部分利用索引
- 升降序混排:先确认版本是否支持降序索引,再决定索引设计
正反例
正反例
-- 索引 (user_id, created_at) 的情况下
-- 用得上:条件列是索引前导,排序列顺延
EXPLAIN SELECT * FROM appdb.orders WHERE user_id = 1 ORDER BY created_at DESC LIMIT 20;
-- 用不上:条件列不在索引里,仍需额外排序
EXPLAIN SELECT * FROM appdb.orders WHERE status = 1 ORDER BY created_at LIMIT 20;
-- 按需建立覆盖索引(把查询列补进索引)
ALTER TABLE appdb.orders ADD INDEX idx_user_created_status (user_id, created_at, status);
-- 确认排序是否消除:看执行计划的附加信息
EXPLAIN FORMAT=JSON SELECT * FROM appdb.orders WHERE user_id = 1 ORDER BY created_at DESC LIMIT 20;执行计划判读
| 信息 | 含义 | 处理建议 |
|---|---|---|
| 出现排序标记 | 需要额外排序步骤 | 调整索引顺序或改写条件列 |
| 键列为空 | 未使用索引 | 检查条件列是否可被索引覆盖 |
| 扫描行数巨大 | 全表或大范围扫描 | 补前导列索引或收窄条件 |
| 附加信息含临时表 | 结果集被物化 | 减少分组与排序组合带来的物化 |
小 LIMIT 的语句有时额外排序反而更快:优化器会权衡,不要看到排序标记就断定有问题,先看实际耗时。
只给排序列建索引而条件列不在索引中:扫描量会比预期大得多,索引形同虚设。