4. 分布式事务解决方案概览
在金融系统中,一笔交易往往横跨账户、账务、清算、风控、通知等多个服务。单体时代的本地事务只能保证单个数据库的 ACID,一旦跨服务、跨库、跨中间件,就必须引入分布式事务方案。本节不深入某一种方案的实现细节,而是先建立一张“全景地图”:把七种主流方案放在同一坐标系里,理解它们各自解决什么问题、付出什么代价、适合什么场景,从而在架构选型时不再凭感觉拍板。
一、核心原理:一致性谱系与代价权衡
所有分布式事务方案,本质上都在回答同一个问题:当多个参与者的操作无法用一个本地事务原子提交时,如何让最终结果保持一致? 区别在于它们选择了一致性的“强度”和“时机”。
- 强一致派:2PC、3PC 追求提交时刻的原子性,通过协调者统一裁决,代价是同步阻塞、协调者单点、性能下降。
- 补偿派:TCC、Saga 放弃“同时成功”,改为“先做、失败再补”,用业务补偿换可用性与吞吐。
- 消息派:Outbox、可靠消息、最大努力通知,把“跨服务调用”转化为“本地事务 + 消息投递”,用最终一致换解耦与弹性。
理解这条谱系后,选型就不再是“哪个方案最好”,而是“哪个方案的代价我能接受”。金融核心账务通常偏强一致,营销、积分、通知类场景则更适合最终一致。
二、方法流程:七种方案的执行骨架
把七种方案拆成“执行骨架”,能快速看清它们的差异。下面用清单形式给出每种方案的关键步骤与核心风险点。
- 2PC:协调者询问所有参与者能否提交 → 参与者锁定资源并投票 → 协调者裁决提交或回滚。风险:同步阻塞、协调者宕机导致悬挂。
- 3PC:在 2PC 前增加 CanCommit 预询问,并引入超时机制,降低阻塞范围,但仍无法彻底解决网络分区下的一致性问题。
- TCC:Try 预留资源 → Confirm 确认扣减 → Cancel 释放预留。要求每个业务操作都实现三个接口,且需处理空回滚、幂等、悬挂。
- Saga:把长事务拆成多个本地事务 T1…Tn,每个 Ti 配一个补偿 Ci;正向失败时反向执行 C(i-1)…C1。适合流程长、参与者多的业务。
- Outbox:业务数据与事件写入同一本地事务的 outbox 表 → 投递器轮询或 CDC 捕获 → 发送到 MQ → 下游消费。核心是“本地事务保证事件不丢”。
- 可靠消息:利用 MQ 的事务消息(如半消息 + 回查),保证“本地事务成功”与“消息可投递”一致。
- 最大努力通知:上游尽最大努力重试通知下游,下游做幂等;仍失败则依赖定时对账与人工干预。
关键判断:如果业务能接受“中间态可见”,优先选 Saga 或消息类方案;如果业务要求“要么全成功要么全不可见”,才考虑 TCC 或 2PC。金融账务中的“冻结/解冻”天然适合 TCC,而“积分发放”更适合可靠消息。
三、案例场景:一笔跨行转账的选型推演
假设用户从 A 银行账户转账到 B 银行账户,涉及本行扣款、跨行清算、对方入账、短信通知四个环节。不同环节对一致性的要求不同:
- 扣款与入账:必须强一致或可补偿,否则会出现“钱扣了没到账”。可采用 TCC:Try 冻结付款方金额,Confirm 扣减并通知清算,Cancel 解冻。
- 清算与对账:跨行清算通常异步,适合 Saga 或可靠消息,配合日终对账兜底。
- 短信通知:允许丢失或延迟,采用最大努力通知即可,失败不影响主交易。
如果强行用 2PC 把短信通知也纳入事务,会导致主交易被通知服务拖慢甚至阻塞,这是典型的“用错方案”。选型的本质是按业务容忍度分层,而不是一刀切。
四、误区与复盘:选型中最容易踩的坑
误区一:认为 TCC 一定比 Saga 强。 TCC 的 Confirm/Cancel 需要业务实现,开发成本高;Saga 虽然补偿逻辑复杂,但对已有服务侵入更小。没有银弹,只有匹配。
误区二:把 Outbox 当成“万能消息队列”。 Outbox 解决的是“本地事务与消息发送的原子性”,不解决消息重复消费、顺序消费,这些仍需下游幂等与去重。
误区三:忽略空回滚与悬挂。 TCC 中 Cancel 先于 Try 到达、或 Try 超时后 Confirm 才到,都会导致数据错乱。必须用事务控制表记录状态,并做幂等校验。
复盘时建议用一张对照表来固化选型结论,避免每次讨论都从头争论。下表可作为团队选型清单:
| 方案 | 一致性 | 侵入性 | 性能 | 典型场景 | 主要风险 |
|---|---|---|---|---|---|
| 2PC | 强一致 | 低(依赖数据库) | 差 | 同构数据库、短事务 | 阻塞、协调者单点 |
| 3PC | 强一致(分区下弱) | 低 | 较差 | 对阻塞敏感的场景 | 网络分区仍可能不一致 |
| TCC | 最终一致(可强) | 高 | 好 | 资金冻结、库存预留 | 空回滚、悬挂、幂等 |
| Saga | 最终一致 | 中 | 好 | 长流程、多参与者 | 补偿逻辑复杂、无隔离 |
| Outbox | 最终一致 | 中 | 好 | 事件驱动、数据同步 | 投递延迟、重复消费 |
| 可靠消息 | 最终一致 | 中 | 好 | 异步解耦、通知 | MQ 可用性依赖 |
| 最大努力通知 | 弱一致 | 低 | 最好 | 短信、推送、日志 | 可能丢失,需对账 |
选型口诀:强一致看 2PC/3PC,资金类看 TCC,长流程看 Saga,解耦看 Outbox/可靠消息,通知类看最大努力通知。先定一致性底线,再定侵入性与成本,最后用对账兜底。