对账要解决什么
只要存在派生存储(搜索索引、缓存、分析宽表、异库副本),与主库之间迟早会出现漂移:同步任务失败、消费者重复消费、程序缺陷与人工修数都会造成差异。对账不是反复全量比对,而是按时间窗口比对计数与关键聚合,把差异缩小到具体窗口与记录,再选择重放变更或按主键重建。核心要求是修复动作幂等、可重复执行、有审计记录。
对账维度
- 总量:按窗口统计条数与关键金额合计,先看是否整体一致
- 分组:按租户、状态、日期分组比对,避免两边同时多算少算互相抵消
- 抽样:对可疑窗口按主键抽样逐条比对字段,定位到具体记录
- 延迟:比对同步位点与主库最新位点的差距,区分「真差异」与「还没同步到」
- 删除:单独统计被删除记录是否已从派生库移除,漏删是长期残留的主要来源
窗口对账与修复(思路示意)
窗口对账与修复(思路示意)
# 1) 按天窗口分别从主库与派生库取统计量
def daily_stats(day):
return db.orders.aggregate([
{ "$match": { "updatedAt": { "$gte": day_start(day), "$lt": day_end(day) } } },
{ "$group": { "_id": "$status", "cnt": { "$sum": 1 }, "amount": { "$sum": "$amount" } } }
])
# 2) 差异窗口内按主键抽样明细,逐条比对字段与更新时间
# 3) 修复:小范围从变更日志重放该窗口,大范围按主键重新同步
# —— 修复动作必须幂等,可安全重跑,并记录影响范围与执行人
常见漂移与修复方式
| 漂移类型 | 典型成因 | 修复方式 |
|---|---|---|
| 缺记录 | 同步任务中断、消息丢失 | 按窗口重放变更日志,或按主键重新投递 |
| 多记录 | 重复消费、无幂等写入 | 先按唯一键去重,再补齐幂等约束防复发 |
| 字段陈旧 | 乱序覆盖、更新未投递 | 带版本号重放,版本低的事件直接跳过 |
| 残留已删数据 | 删除事件未同步 | 按删除清单显式删除,或在派生侧做软删除标记 |
| 延迟造成的假差异 | 同步位点落后 | 先比对位点,确认追赶后再判定差异 |
对账的目标不是「零差异」,而是差异可解释、可修复、有上限:为差异率设阈值并告警,超过阈值时暂停自动切流类动作,先人工确认。
修复任务要与线上写入做并发保护:重建期间若同时接受新写入,可能覆盖刚修复的正确数据,通常需要用限速、窗口加锁或版本比较来避免。