标准答案

  1. 排序至少包含业务排序字段和唯一 ID,例如 `(created_at DESC, id DESC)`。
  2. 下一页条件使用 `(created_at, id) < (:time, :id)` 等与排序一致的比较,不能只比较时间。
  3. 游标应签名或编码,防止客户端篡改查询范围;数据新增和删除会影响实时视图,快照需求要另行设计。

题目解析

游标分页要把排序定义成全序:业务排序字段负责主顺序,唯一 ID 作为 tie-breaker。下一页条件必须与排序方向一致,这样相同时间或分数的记录不会在页间反复出现。

实时数据新增、删除和更新时,分页看到的是不断变化的视图,可能出现轻微重复或漏读。若导出必须一致,应使用快照、固定版本或明确的时间边界。

游标应编码并签名,绑定过滤条件、租户和排序版本,防止客户端修改范围。游标过期或条件变化时返回明确错误,不能悄悄用另一套条件继续读取。

代码示例

降序查询示意:

sql
WHERE (created_at, id) < (:cursor_time, :cursor_id)
ORDER BY created_at DESC, id DESC
LIMIT :page_size;

常见误区

  • 误区:只用 created_at 做游标。改正:加入唯一 ID 等 tie-breaker,并使用与排序一致的比较条件。
  • 误区:下一页仍使用 OFFSET。改正:把上一页最后一条的排序值编码到游标中,避免深分页扫描。
  • 误区:客户端可以自由修改游标中的租户或过滤条件。改正:签名游标并校验它与当前请求上下文一致。

作者信息