giftiaのblog

数据分页:为什么必须 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 是零心智负担的默认选择。


总结

  1. 分页必须排序 —— 不排序的分页叫抽奖
  2. 排序字段必须唯一 —— 用 id 最省心
  3. 默认 ASC,DESC 要有理由 —— 逆序下新增数据会造成偏移分页重复