数据同步任务如何保证幂等?
简化版
同步任务幂等的核心是稳定事件身份、目标端条件写和原子推进进度。 重复处理同一事件必须得到相同结果;旧版本事件不能覆盖新版本,删除用带版本墓碑。 Checkpoint 只在目标写入已确认后推进,并能处理“目标成功但确认丢失”的重复执行。
详细版
事件包含 eventId、entityId、sourceVersion、operation 和 sourcePosition。
目标表保存 lastSourceVersion,仅当 incomingVersion > lastSourceVersion 时才写。
对增量加法等非幂等动作,在同一事务插入 eventId 去重记录并更新业务数据。
多个分区分别维护有序 checkpoint,不能用一个全局 offset 跨分区确认。
完整版教学
一、为什么重复不可避免
至少一次投递、网络超时和 Worker 崩溃都会导致重放。 目标写成功后进程在提交 checkpoint 前退出,恢复后必然再处理。 正确系统把重复视为正常路径。
幂等不是尽量不重复,而是重复发生后结果不变。
二、事件身份
eventId: globally stable
entityId: O123
sourceVersion: 18
operation: UPSERT
sourcePosition: partition-3/offset-9001
重试沿用同一 eventId,不能每次重新生成。
三、状态覆盖同步
目标只在 incomingVersion 大于当前版本时覆盖。
| 输入 | 当前 | 动作 |
|---|---|---|
| 19 | 18 | 更新 |
| 18 | 18 | 重复,忽略 |
| 17 | 18 | 旧事件,拒绝 |
版本相同但内容不同应告警为源分叉。
四、增量动作去重
余额加 10 重复两次会加 20,不能只用覆盖。 在同一目标事务中先插入唯一 eventId,再执行增量。 唯一键冲突说明已处理,返回原结果。 去重记录与业务更新分两个事务仍有失败窗口。
五、Checkpoint 顺序
先写目标,再提交 source position。 先提交 offset 后写目标,崩溃会永久漏数据。 目标成功但 offset 未提交会重复,由幂等吸收。 每个 source partition 分别维护连续确认位置。
六、删除与全量
删除用 tombstone 和 sourceVersion,防迟到 upsert 复活。 全量同步可写 staging,校验后原子切换。 分页使用稳定主键游标,避免 offset 分页漏重。 runId 只是运行标识,业务幂等仍依赖实体版本或事件键。
七、观测验证
统计 received、applied、duplicate、stale、conflict 和 failed。 监控 checkpoint lag、最老未确认事件、版本跳跃和死信。 在目标提交前后分别杀进程,验证不漏且无重复副作用。 定期对账检测源事件缺失或映射错误。
八、常见误区与追问
- 误区:MQ Exactly Once 让业务天然幂等。 外部数据库仍需事务与去重。
- 误区:先提交 offset 更省事。 崩溃会永久漏数据。
- 误区:所有操作都可用 upsert。 增量动作需要事件去重。
- 误区:runId 就是业务幂等键。 批次重跑会变化。
- 追问:成功响应丢失怎么办? 重放同一 eventId。
- 追问:乱序怎么办? sourceVersion 条件写。
- 追问:如何防删除复活? 保留带版本墓碑。
九、加强记忆
- 稳定 eventId 和 entityVersion。
- 覆盖写用版本 CAS。
- 增量写用事务内事件去重。
- 目标确认后再推进分区 checkpoint。
- 墓碑处理删除,对账补盲区。