ORDER BY 走索引是什么
当 ORDER BY 的列顺序与索引顺序一致(包括排序方向)时,数据库可以按索引顺序读取,省掉额外的排序步骤;再配合 LIMIT,读够需要的行数就能停下,是最划算的优化手段之一。判断方法直接看执行计划:没有排序节点说明排序被索引满足;PostgreSQL 里表现为 Index Scan 且没有 Sort,MySQL 则表现为使用索引的顺序扫描。前提是排序列必须是某个索引的最左前缀,且方向能对上。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 均支持 |
|---|---|
| 前提条件 | ORDER BY 的列序列与索引前缀一致,排序方向也需匹配 |
| LIMIT 收益 | 按序读索引,读到足够行数即停止,避免全量排序 |
| MySQL 降序索引 | 8.0 起支持真正的降序索引,混向排序需要对应定义的索引 |
| PostgreSQL 细节 | 可反向扫描索引;NULLS FIRST / LAST 要与索引定义对应 |
典型使用场景
- 列表页按创建时间倒序取前 20 条
- 消息流按 id 倒序拉取增量数据
- 排行榜按分数取 TopN,且分数列上有索引
- keyset 分页的定位查询依赖索引顺序,免排序是它的性能基础
- 排查 filesort / 临时排序,判断是否值得补一条复合索引
MySQL 验证
MySQL 验证
# 索引:idx_user_created (user_id, created_at)
SELECT id, total
FROM orders
WHERE user_id = 100
ORDER BY created_at DESC
LIMIT 10;
# TREE 计划中出现反向索引扫描且没有 Sort 节点:
# -> Limit: 10 row(s)
# -> Index lookup on orders using idx_user_created (user_id=100) (reverse)
# 排序键与索引顺序不一致:无法免排序
SELECT id FROM orders ORDER BY total DESC LIMIT 10;PostgreSQL 验证
PostgreSQL 验证
-- 索引:CREATE INDEX idx_orders_user_created ON orders (user_id, created_at DESC);
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, total
FROM orders
WHERE user_id = 100
ORDER BY created_at DESC
LIMIT 10;
-- 期望出现 Index Scan using idx_orders_user_created,并且没有 Sort 节点
-- 若看到 Sort + Seq Scan,说明索引没被利用,需要核对列顺序与方向计划中的排序信号
| 现象 | 含义 | 处理方向 |
|---|---|---|
| 没有排序节点 | 排序由索引满足 | 保持现状 |
| 出现 Sort / filesort | 需要额外排序 | 核对索引列顺序与排序方向 |
| Sort 且输入行数很大 | 先全量排序再截断 | 补匹配排序键的复合索引 |
| 走了索引但回表次数高 | 按索引顺序随机回表 | 考虑覆盖索引或调整取舍 |
MySQL 8.0 才引入真正的降序索引,ORDER BY a ASC, b DESC 这类混向排序只有建了对应方向的索引才能消除 filesort,早期版本所有索引都按升序存储但可反向扫描。
低区分度列按序翻页要留神:即使走索引,随机回表成本也可能让优化器改回全表扫描加排序,深分页应改用 keyset 分页而不是继续加大 OFFSET。