对账脚本按 updated_at 增量拉取,时间窗口写的是闭区间,每次从上次的最大时间开始,边界那一秒的数据被前后两次都拉到。任务每天跑一次,一个月重复入账 37 条,金额差了 2100 元。改成半开区间之后仍有一批重复。
一开始怀疑是「拉取后按 id 去重但表上没有唯一约束」,查了一圈,方向不对。
排查方向其实有限,先把可能性排一排。时间窗口用了闭区间,边界值被前后两次都包含。另一种情况是updated_at 精度只到秒,同一秒的多条记录无法区分。
另外,任务重跑时没有记录已处理到的水位。
动手时先看边界那一秒有几条记录
mysql -e "SELECT COUNT(*) FROM orders WHERE updated_at='2026-11-15 00:00:00'" app
随后改成左闭右开区间拉取
python -c "print('where updated_at >= t0 and updated_at < t1')"
接着用时间加主键做复合水位
python -c "print('watermark = (updated_at, id)')"
末了验证重复条数为零
mysql -e "SELECT request_id, COUNT(*) c FROM recon GROUP BY request_id HAVING c>1 LIMIT 5" app
恢复的判定其实很直接。连续跑三次对账任务,入库条数与源表在窗口内的条数完全一致,重复记录查询返回空。
补一句。增量同步的边界要用半开区间并叠加主键做水位,只靠秒级时间戳,同一秒多条数据时必然重复。拖得越久,排查成本越高,因为现场状态会被后续操作覆盖。
