跳到主要内容

从业务不变量到一致性方案

分布式一致性方案应从必须始终成立的业务规则开始。先写清哪些状态不能同时出现、错误能持续多久,以及失败后能否补偿,再决定把数据放进一个事务、预留资源,还是通过事件最终收敛。

1. 不变量描述不能被破坏的规则

“订单、库存和支付保持一致”仍然太宽。可以继续拆成:

  • 同一支付请求最多产生一笔有效扣款。
  • 已确认库存不能低于零。
  • 已取消订单不能再次进入发货状态。
  • 每笔资金变化都有可核对的账务记录。
  • 用户看到成功后,结果不能在没有说明的情况下消失。

不同规则的错误成本不同。推荐结果短暂陈旧和重复扣款不能使用同一套一致性目标。

2. 先缩小需要原子提交的范围

最简单的可靠边界仍是单个权威存储中的本地事务。若订单状态和库存扣减必须原子判断,可以先考虑由同一服务和数据库拥有它们,而不是立即拆成两个服务再引入分布式事务。

服务边界应服从数据所有权和业务变化。为了部署独立而拆开强耦合不变量,会把进程内事务变成跨网络协调,并增加每次变更的故障状态。

3. 区分同步确认与异步完成

接口向用户返回成功前必须已经满足哪些条件,需要明确:

  • 同步强条件:例如幂等请求已登记、支付授权已取得、库存已预留。
  • 异步后续工作:例如积分发放、搜索索引、通知和分析事件。

把可重试的派生工作移到事件链路,可以缩短同步事务。但不能把关键扣款也标记成“最终一致”后不定义中间状态和失败处置。

4. 为不一致窗口设置上限

最终一致需要可验证的时间目标,例如 99.9% 的索引更新在 30 秒内完成,超过 5 分钟进入告警。没有时限和监控的“最终”只是未知状态。

还要定义窗口内的用户体验:订单已支付但积分未到账时显示处理中,还是返回旧余额;是否允许用户重复点击;客服如何查询真实进度。

5. 根据资源能力选择协调方式

  • 数据都能加入同一事务资源,并且可以接受持锁与协调可用性时,可考虑 2PC/XA。
  • 业务资源可以显式预留、确认和释放时,可考虑 TCC。
  • 长流程允许中间结果可见,并且每步有业务补偿时,可考虑 Saga。
  • 本地事务提交后需要可靠产生异步事件时,使用 Outbox 或 CDC。
  • 下游只是可重建投影时,使用幂等事件、版本和对账最终收敛。

方案名称不能替代故障分析。每一种都要画出协调者、参与者和消息在每个断点失败时留下什么状态。

6. 补偿能力限制可接受流程

数据库更新可以通过反向操作修正,已经发送的邮件无法收回,外部支付退款也不是原扣款的物理回滚。补偿可能失败、收取手续费,或需要人工审批。

不可逆步骤应尽量放在流程后部,并在执行前完成必要检查。不能可靠补偿的风险要通过预留、额度、人工确认或重新划分权威边界降低。

7. 所有异步边界都要可追踪

为每个流程建立稳定业务 ID 和状态机,记录当前步骤、尝试次数、下一次重试、最后错误和关联事件。中间状态对运维和用户可见,才能在故障后恢复。

仅依靠日志拼接流程不够可靠。日志可能采样、过期或缺失,业务状态应存入可查询的权威记录。

8. 用故障矩阵验证选择

至少逐项检查:

  1. 本地事务提交前后进程崩溃。
  2. 消息发布成功但确认丢失。
  3. 参与者超时但实际已完成。
  4. 补偿重复、乱序或永久失败。
  5. 协调器长时间不可用。
  6. 人工重放历史事件。

对每个断点写出检测信号、自动恢复、最大风险窗口和人工入口,再判断方案是否满足不变量与 SLO。

9. 常见问题

9.1 一致性要求高是否一定选择 2PC

不一定。先看能否把强不变量收回同一个本地事务,或者用预留和条件更新缩小同步范围。2PC 需要参与资源支持,并会增加持锁时间和协调故障影响。

9.2 使用消息队列是否就实现最终一致

没有。还需要保证本地变化能可靠产生消息、消费者幂等、失败可重试、乱序不覆盖新状态,并通过对账发现静默丢失。消息队列只承担传输的一部分。

10. 面试题

10.1 如何根据业务选择分布式事务方案

出现公司:阿里巴巴

考察重点

  • 业务不变量、数据所有权和同步成功条件。
  • 2PC、TCC、Saga、Outbox 的适用前提。
  • 不一致窗口、补偿和故障恢复证据。

相关内容:第 1 节“不变量描述不能被破坏的规则”至第 8 节“用故障矩阵验证选择”。

参考回答

我会先写出不能破坏的不变量,例如不重复扣款、已确认库存不为负,并确定哪些条件必须在接口返回成功前满足。能由一个服务和数据库拥有的数据优先放进本地事务;派生索引、通知和积分可以异步,但要给出最大不一致时间。

多个支持事务协议的资源且能接受协调和持锁成本时才考虑 2PC;可显式预留资源时用 TCC;长流程且允许可见中间状态时用 Saga;本地提交后可靠发布事件用 Outbox 或 CDC。最后逐个注入提交、超时、消息确认和补偿失败,确认状态可追踪、操作幂等、能够对账与人工修复。