数据分页:为什么必须 ORDER BY id
·
giftia
一、为什么要排序
SQL 标准不保证 SELECT * FROM t 的返回顺序。不排序的分页是不可靠的。
两次分页请求之间若发生 UPDATE 触发页面重组,物理存储顺序可能改变:
请求 page=1:LIMIT 10 OFFSET 0 → [A,B,C,D,E,F,G,H,I,J]
请求 page=2:LIMIT 10 OFFSET 10 → [C,D,K,L,M,N,O,P,Q,R]
结果:C、D 重复出现,A、B 永远丢失。
ORDER BY 的本质是给每行一个在全量数据中固定的位次。有了固定位次,OFFSET 才能精确定位到"第 11~20 行",不受增删改影响。
二、为什么用 id
单调递增,新增不影响已有分页
id 自增,新数据追加到末尾,不会挤占已翻过的页。而递增的 ORDER BY id ASC,新数据永远在最后一页之后。
值唯一,排序无歧义
若排序字段可能重复(如 created_at),两行并列时先后顺序不确定,可能某行出现在两页。id 天然唯一,没有这个问题。
需要业务字段排序时,用 id 兜底:ORDER BY created_at, id。
自带聚簇索引,排序零开销
ORDER BY id 直接走主键索引顺序扫描,不需要额外排序。而 ORDER BY name 需要建索引 + 回表。
不可变
id 写入后永不改变。而 ORDER BY updated_at 每次更新顺序就变,分页直接乱套。
三、为什么不能逆序
ORDER BY id DESC 在稳定性上和 ASC 等价,但有一个致命问题:
新增数据会插入排序头部,导致偏移分页重复:
T1 第一页(DESC):id [30,29,...,21]
T2 新增 id=31
T3 第二页(DESC):排序变为 [31,30,...,21,20,19,...]
取 10 条偏移 10 → id [21,20,...,12]
id=21 在 T1 和 T3 都出现了——重复。
而 ORDER BY id ASC 下新数据追加末尾,不影响已查过的页。
逆序什么时候用
| 场景 | 排序 |
|---|---|
| 管理后台列表 | ORDER BY id ASC |
| 消息列表(最新在上) | ORDER BY id DESC |
| 无限滚动加载历史 | 游标 + ORDER BY id DESC |
逆序有场景,只是要有明确理由。无特殊需求时 ASC 是零心智负担的默认选择。
总结
- 分页必须排序 —— 不排序的分页叫抽奖
- 排序字段必须唯一 —— 用 id 最省心
- 默认 ASC,DESC 要有理由 —— 逆序下新增数据会造成偏移分页重复