分布式事务在金融场景的取舍
对比 2PC、TCC、Saga 与本地消息表在金融场景的适用边界,并说明为什么对账才是最终防线。
金融系统聊分布式事务,很容易陷入"选哪个框架"的讨论。但真实的约束是:钱不能错,且必须能查清为什么错。这两条决定了方案选择的顺序和别的行业不太一样。
金融场景的真实约束
- 不允许静默不一致。可以暂时不一致,但必须有机制发现并修复,不能出现谁也不知道差在哪的状态。
- 必须能人工干预。任何自动补偿机制都有失效的可能,最终要留一条人工处理通道,且要有审计留痕。
- 联机响应时间有硬指标。核心交易通常要求 100ms 内,多阶段协调的额外网络往返基本吃不下。
- 补偿不总是可行。已经出账到人民银行的报文没法回滚,只能发反向报文,业务语义完全不同。
最后一条最容易被忽略:很多补偿型方案假设操作可逆,但金融业务里大量操作在真实世界里是不可逆的。
几种方案的适用边界
- 2PC / XA:强一致,但同步阻塞、协调者单点、锁持有时间长。只在同库多表或同一数据库集群内使用。跨服务用它,第一次网络抖动就会看到大批悬挂事务。
- TCC:一致性和性能都不错,代价是每个参与方要写三份逻辑(try / confirm / cancel),且必须自己处理空回滚、悬挂、幂等。开发量大约是普通接口的三倍,适合参与方少且业务确定的核心链路。
- Saga:适合长流程、多参与方,比如开户流程里的证件核验、账户开立、卡片制发。要求每步都有语义补偿,且能接受中间态对外可见。
- 本地消息表 + 可靠投递:把消息写入和业务操作放在同一个本地事务,再由后台任务投递。实现简单、无外部依赖、可靠性靠数据库保证。缺点是只能做最终一致。
我们的默认选择:本地消息表 + 对账
绝大多数场景我们用本地消息表。理由很朴素:它没有引入新的中间件,所有状态都在业务库里,出问题时排查路径最短。
配套三件事一个都不能省:
- 下游幂等。用业务唯一键做唯一索引,重复消息直接忽略。
- 重试上限加人工队列。无限重试等于把问题藏起来。
- 日终对账。这才是最终防线。
我的结论是:对账做扎实了,分布式事务方案可以选最简单的那个;对账没做,再精巧的 TCC 也只是把不一致藏得更深。
选型的顺序应该是:能不拆服务就不拆,同库能解决就用本地事务;必须跨服务时优先最终一致加对账;只有确实无法接受中间态的核心链路才上 TCC。别一开始就找框架。