列表翻页出现重复记录,可能是排序键不唯一或数据在分页期间变化。分页需明确顺序和读取范围。
OFFSET 依赖结果位置,插入删除会移动位置。以稳定唯一组合键做游标分页可降低深翻页成本,但并发变化仍需定义语义。
一、问题的范围与前提
数据库连接、事务、SQL 执行和数据结果应分别验证。一次连接成功不证明事务可写,也不证明原有查询在不同数据库上结果相同。应从实际业务入口取得证据,记录角色、数据库、事务范围与查询条件,再分析执行计划或连接池。
二、技术分析与方案取舍
当多个记录具有同一 created_at,仅按时间排序会让相邻页边界不确定。加入唯一 id 提供稳定次序,游标保存两项条件。若遍历期间允许修改排序字段,仍可能重新出现或遗漏,应选择快照、固定截止时间或业务可接受的规则。深分页性能和完整遍历正确性各自测试,不能只看到 LIMIT 返回100行就认为分页完整。
测试数据先覆盖正常输入、边界值和并发条件。精度、空值、时间和关联约束需要固定规则,更新语句应处于明确事务中。对于会产生写入的试验,使用独立测试环境并准备恢复方法;只读执行计划也应核对查询是否会调用带副作用的函数。
三、实现与排查的顺序
- 为排序补充唯一键。
- 比较 OFFSET 与游标。
- 确定筛选和快照范围。
- 测试并发插入删除。
按这个顺序保留中间结果,可以区分输入条件、处理过程和最终输出。前一阶段的条件不成立时,先解释对应错误,再继续后面的试验;单次返回成功不足以覆盖整条链路。
最小示例
1
2
3
4
5
SELECT id, created_at
FROM example_event
WHERE (created_at, id) > (:last_time, :last_id)
ORDER BY created_at, id
LIMIT 100;
具名参数代表由数据库驱动绑定的值,不应字符串拼接。组合游标用于示意稳定排序,目标数据库需支持相应行比较或改成等价条件。并发变化和排序键可变时,还需定义遍历范围。
四、验证和结果解释
查询结果可以按主键集合、字段值和关联关系核验,不宜仅比较总行数。性能记录应同时保留参数、数据规模、缓存状态与并发条件。错误时检查服务端实际连接和活动事务,避免只观察另一台实例或另一份会话。
| 需要验证的结果 | 保留的对照 | 不能由此推出的结论 |
|---|---|---|
| 遍历不重复漏项 | 固定输入与配置、对应输出 | 一次成功代表所有条件成功 |
| 相同时间次序稳定 | 中断前后状态与错误阶段 | 没有报错代表结果正确 |
| 深分页成本可观察 | 恢复后的同条件复测 | 临时恢复代表根因已经消失 |
重试、刷新连接和改变数据库角色属于不同操作。异常后先确定处理结果,再决定重复执行;跨外部接口的业务还需要幂等和补偿。备份恢复与数据迁移必须演练,回到旧程序版本不自动恢复被修改的持久数据。
五、适用边界与结论
排序字段可变时要额外约束,客户端不能跨不同筛选条件复用游标。
这条路径把“为排序补充唯一键”与“测试并发插入删除”连在一起,中间保留可复查的条件。实施时优先确认“遍历不重复漏项”,再检查“深分页成本可观察”;两者都满足,才能在当前样本与配置范围内评价方案。设备、数据或软件版本改变以后,相关假设需要重新验证。