分布式事务补偿:失败后能收场,比强一致更现实

分布式事务补偿:失败后能收场,比强一致更现实

一、不是所有业务都需要强一致

分布式系统里,订单、库存、积分、通知、账单经常跨服务。很多团队一遇到一致性问题,就想上强事务。但强一致成本高、耦合重、可用性差。很多业务更适合最终一致和补偿机制。

补偿不是偷懒,而是在现实约束下设计可恢复流程。

二、先识别业务步骤

flowchart TD A[创建订单] --> B[扣库存] B --> C[扣优惠券] C --> D[发送通知] D --> E[完成] C --> F[失败补偿]

每个步骤都要知道成功后如何补偿。扣库存可以释放,优惠券可以退回,通知可能无法撤回,只能追加说明。

saga_step: action: deduct_coupon compensation: refund_coupon idempotent: true

没有补偿动作的步骤,要标记为高风险。

三、状态机比本地事务更重要

enum SagaState { CREATED, STOCK_DEDUCTED, COUPON_DEDUCTED, COMPLETED, COMPENSATING, FAILED }

Saga 流程必须持久化状态。服务重启后,要知道执行到哪一步,哪些动作需要继续,哪些需要补偿。

只靠方法调用栈管理分布式流程,一旦中途失败就会失去上下文。

四、补偿也要幂等

补偿动作可能被重复执行。释放库存、退回优惠券、撤销冻结金额,都必须设计幂等。否则修复失败会制造新的不一致。

compensation_policy: idempotency_key_required: true retry_with_backoff: true manual_review_after_max_retry: true

补偿失败后不要无限重试。达到上限要进入人工处理,并保留足够上下文。

还要区分技术失败和业务失败。网络超时可以重试,业务规则不允许就不能硬补偿。错误分类不清,补偿流程会很危险。

最后,补偿流程要可观测。每个 Saga 的状态、耗时、失败步骤、补偿次数都要能查。最终一致不是最终没人管。

补偿设计还要考虑用户体验。订单显示“处理中”多久,失败后用户看到什么,补偿完成后是否通知,都要和技术状态机对应。否则后台最终一致了,前台用户仍然困惑。

saga_user_state: processing: show_pending compensating: show_recovering failed_final: show_contact_support

还要有超时策略。某个 Saga 卡在中间状态太久,系统应该自动扫描并推进补偿或告警。不能等待用户投诉才发现流程停住。

对账任务也很关键。最终一致系统必须定期比较订单、库存、优惠券、账务等结果,发现漏补偿或重复补偿。没有对账,补偿机制很难让人放心。

最后,补偿动作要经过权限和审计。自动补偿也是业务变更,不能因为是系统触发就跳过记录。

补偿流程还要考虑并发。用户可能取消订单,系统同时在补偿库存;支付回调可能迟到,Saga 又在走失败流程。如果没有状态锁或版本号,补偿可能和正常流程互相覆盖。

saga_concurrency: use_state_version: true reject_stale_transition: true lock_by_business_key: true

状态转换要原子化。每一步从一个状态变到另一个状态时,都要确认当前状态仍符合预期。否则重复消息、迟到事件和人工操作会把流程打乱。

补偿机制的测试策略也值得深入讨论。传统的单元测试、集成测试很难完整覆盖分布式补偿场景,因为涉及到网络分区、部分失败、并发冲突、超时等复杂情况。一种有效的方法是"故障注入测试":在 Saga 执行过程中,模拟特定步骤失败、消息重复、数据库不可用等情况,验证补偿逻辑是否能够正确回滚。可以在测试环境中使用 Chaos Mesh 或类似工具,在 Saga 的不同阶段注入故障,观察系统的行为。这种测试虽然成本较高,但对于关键业务链路来说,是验证补偿机制正确性的重要手段。

还有一个现实问题:补偿失败的"终极处理"。即使设计了完善的补偿机制和重试策略,仍然可能存在某些情况,补偿动作本身持续失败(比如补偿依赖的外部系统永久下线)。这时候需要"人工兜底流程":系统将无法自动补偿的事务标记为"需要人工处理",通知相关人员,并提供足够的上下文(原始操作、已执行的补偿步骤、失败原因等)。在设计时就要考虑这个"最后一道防线",包括人工处理界面的设计、处理记录的审计、以及处理后的状态对齐确认。假装所有补偿都能自动化成功,是不负责任的系统设计。

五、总结

分布式事务补偿要把业务步骤、状态机、补偿动作、幂等和人工兜底设计清楚。

失败后能收场,比强行追求所有场景强一致更现实,也更容易落地。