深浅主题
分页查询重复与遗漏
问题现象
翻页时同一条数据出现两次,或某条记录始终看不到;同一个 SQL 多次运行顺序不同。
适用环境
MySQL 8、MyBatis;排序字段可能重复,列表持续有写入。
本文是可复用的排障示例,不代表某次个人生产事故。命令中的名称、账号和数据均为示例,执行前请替换为已确认的目标。
复现或诊断步骤
- 检查 ORDER BY 是否有唯一末级排序键。
- 固定数据集重复执行,区分排序不稳定与并发插入造成的偏移变化。
- 核对一对多 JOIN 是否先放大了行数,再被分页;比较 count 查询与数据查询的条件。
原因分析
只按 created_at 排序时,同一时间的记录没有确定顺序。OFFSET 分页遇到新增或删除会整体移动位置。先 JOIN 再分页也可能把同一业务实体拆到多页。
修复示例
sql
-- 第一页
SELECT id, created_at, title
FROM article
ORDER BY created_at DESC, id DESC
LIMIT 20;
-- 下一页携带上一页末行的两个排序值
SELECT id, created_at, title
FROM article
WHERE created_at < :cursor_time
OR (created_at = :cursor_time AND id < :cursor_id)
ORDER BY created_at DESC, id DESC
LIMIT 20;建立与过滤和排序匹配的组合索引,游标必须包含完整排序值。涉及多个关联表时,先确定主实体 ID 页,再加载关联数据。需要报表级一致结果时采用明确快照或导出任务,游标本身不能冻结被修改的排序字段。
验证方法
准备同一时间的多条记录,遍历所有页核对 ID 集合。翻页期间插入新记录、删除旧记录并记录所选一致性语义;测试 cursor 参数校验与时间精度。
常见误区
DISTINCT 并非万能修复,可能改变统计和性能。稳定 ORDER BY 可以解决静态数据排序,不能保证 OFFSET 在并发写入下无遗漏。