幂等、对账与人工修复
自动重试只能处理已知且暂时的失败,对账用于发现系统没有主动报告的差异,人工修复则处理无法安全自动决定的状态。三者共同构成分布式流程的恢复能力。
1. 每一步使用稳定业务标识
全局流程 ID、步骤 ID、支付单号和事件 ID 应在首次创建后保持不变。请求重试、消息重放和人工补单都携带原标识,让参与者识别同一个业务动作。
幂等记录与业务变化尽量放在同一本地事务,并通过唯一约束或条件状态转换实现。只在 Redis 中设置短期标记,无法覆盖数据提交窗口和长期历史重放。
2. 状态机限制合法转换
订单可以从 PENDING 进入 PAID 或 CANCELLED,但不能从 CANCELLED 因迟到消息直接回到 PAID。每个命令说明允许的前置状态、产生的新状态和重复到达时的结果。
状态转换使用版本或当前状态作为 SQL 条件。受影响行数为 0 时,再读取当前状态区分“已经完成”“版本过期”和“非法操作”。
3. 重试按错误类型和风险执行
网络 reset、限流和短暂数据库不可用可以有限退避重试;参数错误、账户冻结和 schema 不兼容需要修复输入或代码。结果未知的外部调用先按幂等 ID 查询,不能立即重复执行。
每个步骤记录尝试次数、下一次时间、错误类别和最终结果。超过自动上限后进入明确的待处理状态,避免无限重试占满工作队列。
4. 对账比较权威事实与派生结果
先确定每类数据的 source of truth。常见对账包括:
- 支付渠道流水与内部支付记录。
- 订单已支付状态与账务分录。
- 数据库记录与搜索索引版本。
- 已提交业务变化与 Outbox 事件。
- 对象存储文件与数据库元数据。
比较可以使用业务主键、金额汇总、版本、计数和校验值。只比较总数可能让一条多、一条少互相抵消,关键业务要能下钻到具体记录。
5. 增量扫描必须可重复
对账任务按稳定游标和时间区间分片,记录本次快照范围与进度。源数据持续变化时,使用 updated_at 要处理相同时间戳和延迟提交,通常保留重叠窗口并依靠幂等去重。
周期全量核对可以发现增量逻辑长期遗漏。任务失败后从已提交检查点继续,不能因为重跑而再次产生修复副作用。
6. 自动修复只处理确定性差异
搜索索引缺失且数据库记录明确存在,可以从数据库重建;重复通知通常不能“撤回”;资金差异也不能简单以任一边覆盖另一边。
自动规则应明确:
- 哪一侧是权威状态。
- 修复操作是否幂等。
- 是否会覆盖之后的合法变化。
- 最大金额、数量或时间范围。
- 修复后如何再次核对。
超出阈值或存在多种合理解释时转人工,不让脚本猜测业务事实。
7. 人工入口也要受状态机约束
人工操作通过受控工具发出带操作者、原因和工单号的命令,复用正常业务校验和幂等机制。不要让运维直接修改多张表后再口头通知其他团队。
高风险修复使用双人审批、金额上限和预览 diff。每次操作产生不可变审计事件,并能查询执行结果和后续补偿。
8. 修复流程需要演练
定期注入事件丢失、重复、乱序、消费者永久失败和外部结果未知,验证告警能发现、对账能定位、重放不会重复副作用,人工工具也能在权限和时限内完成。
恢复时间应纳入 SLO。备有脚本但从未在接近生产的数据量上运行,不能证明系统可恢复。
9. 常见问题
9.1 消息系统已经保证 at-least-once,为什么还要对账
at-least-once 只描述 broker 与消费者的投递关系。生产端可能没有生成事件,消费者代码可能错误地确认,人工数据修改也可能绕过链路。对账从业务结果反向发现这些静默差异。
9.2 人工修复是否说明架构设计失败
高风险系统需要保留无法自动判断时的人工边界。判断这项设计是否可靠,要看人工操作是否可审计、受约束并且可验证;系统不能假设所有异常都能自动恢复。
10. 面试题
10.1 分布式流程补偿多次失败后,如何发现并安全修复
出现公司:阿里巴巴(淘天)
考察重点
- 稳定 ID、状态机和有限重试。
- 权威数据、增量对账和确定性修复。
- 人工审批、审计和恢复验证。
相关内容:第 1 节“每一步使用稳定业务标识”至第 8 节“修复流程需要演练”。
参考回答
每个流程和步骤先有稳定 ID,参与者用唯一约束和条件状态转换保证重试幂等。自动重试按错误类别退避并设上限,结果未知时先查询;超过上限进入可查询的人工状态,而不是无限循环。
系统明确每类数据的 source of truth,按稳定游标周期比较业务记录、账务、事件和派生索引,并让任务本身可恢复。只有权威侧明确、操作幂等且不会覆盖新变化的差异才自动修复;资金或含混冲突进入带审批、预览和审计的工具。修复后再次对账,并通过故障演练验证发现与恢复时间。