分布式事务的困境:2PC、TCC、Saga 的优缺点对比

聊到分布式系统,事务问题永远绕不开。我早年做金融系统时,被分布式事务坑过好几次。说白了,在单库时代,ACID 是铁律;到了微服务时代,数据散落在几十个服务里,你再想用一把锁搞定所有事情,基本不现实。

这一节,我把主流的分布式事务方案——2PC、TCC、Saga——掰开揉碎讲清楚。它们各自的优缺点,以及为什么最终一致性成了大家的首选。

2PC:两阶段提交,理想很丰满

2PC 是最早的分布式事务方案。它有个协调者,负责问所有参与者:“准备好了吗?” 如果都说“准备好了”,协调者再发一条“提交”。

听起来简单,对吧?但我在项目中遇到过它的致命伤。

核心流程

  • 第一阶段(准备):协调者问所有节点,节点锁住资源,返回“可以提交”。
  • 第二阶段(提交/回滚):协调者收到全部“可以”,就发提交;否则发回滚。
维度 说明
优点 强一致性,实现简单,数据库原生支持(XA 协议)
缺点 同步阻塞,协调者单点,脑裂风险,性能差

你想想看,2PC 在第二阶段如果协调者挂了,所有参与者都卡在“已准备”状态,资源锁着不放。我见过一个生产事故,就是因为协调者宕机,数据库连接池被占满,整个系统挂了半小时。

避坑指南

我曾经在一个高并发订单系统里尝试用 2PC,结果压测时 TPS 直接掉到个位数。2PC 只适合低并发、短事务、参与者少的场景。别在高流量核心链路里用它。

TCC:业务补偿,灵活但复杂

TCC 是 Try-Confirm-Cancel 的缩写。它把每个操作拆成三个阶段:预留资源(Try)、确认执行(Confirm)、取消回滚(Cancel)。

说白了,TCC 是业务层面的 2PC。它不依赖数据库锁,而是靠业务代码实现补偿。

阶段 动作 示例(账户扣款)
Try 预留资源 冻结 100 元,状态标记为“冻结中”
Confirm 确认执行 扣减冻结金额,状态改为“已扣款”
Cancel 回滚补偿 解冻金额,状态恢复“可用”

优缺点

  • 优点:不锁数据库资源,性能比 2PC 好,适合高并发。
  • 缺点:业务侵入性强,每个接口都要写 Try/Confirm/Cancel 三套逻辑。空回滚、幂等、悬挂问题处理起来很头疼。

我记得有一次做支付系统,TCC 的 Cancel 逻辑里忘了处理幂等,结果重复回滚导致用户账户多了一笔钱。嗯,那天的复盘会开了三个小时。

我的建议

TCC 适合对一致性要求高、但能接受短暂不一致的场景。比如账户转账、库存扣减。但一定要做好幂等和空回滚防护。

Saga:长事务的救星

Saga 是另一种思路。它把一个长事务拆成多个本地事务,每个本地事务都有对应的补偿操作。如果某个步骤失败,就反向执行补偿。

Saga 有两种模式:

  • 编排模式(Choreography):每个服务完成后发事件,下一个服务监听事件触发。没有中心协调者。
  • 协调模式(Orchestration):有一个协调者服务,负责告诉每个服务该做什么。
维度 编排模式 协调模式
复杂度 低,服务间通过事件解耦 高,协调者承担所有逻辑
可追踪性 差,流程分散在各服务日志 好,协调者记录完整流程
适用场景 简单链路,服务少 复杂链路,需要集中管控

我个人习惯用协调模式。虽然多了一个服务,但出问题时排查起来方便很多。我曾经在编排模式里追一个补偿失败的问题,翻了五个服务的日志才找到原因。

注意

Saga 没有隔离性。如果两个 Saga 事务并发操作同一数据,可能出现脏写。你需要业务层面做防重和幂等控制。

为什么消息最终一致性成了主流?

上面三种方案,各有各的痛点。2PC 太慢,TCC 太累,Saga 太绕。那有没有更简单的办法?

有。就是消息最终一致性。

核心思想很简单:

  1. 本地事务和发消息放在一个事务里(比如 Outbox Pattern)。
  2. 消息可靠投递到消息队列。
  3. 下游服务消费消息,执行自己的本地事务。
  4. 如果失败,通过重试+幂等保证最终一致。

你想想看,这比 2PC 轻量多了。没有锁,没有协调者,没有复杂的补偿逻辑。只要保证消息不丢、不重复消费,数据最终能对齐。

为什么它成了主流?

  • 性能好:没有同步阻塞,吞吐量高。
  • 解耦强:服务之间只依赖消息,不依赖 RPC。
  • 容错好:消息队列本身有持久化、重试、死信机制。
  • 实现简单:相比 TCC 的三套代码,消息方案只需要一个 Outbox 表和一条消息发送逻辑。

当然,它也有代价。最终一致性意味着在某个时间窗口内,数据是不一致的。比如用户下单后,积分服务可能延迟几秒才更新。但大部分业务场景能接受这个窗口。

我的经验

我在电商系统里用消息最终一致性处理订单和库存同步。高峰期每秒几千单,消息队列稳得很。偶尔有消息积压,但从来没丢过数据。相比之前用 TCC 时天天调补偿逻辑,这简直是解放。

三种方案对比一览

方案 一致性 性能 复杂度 业务侵入 适用场景
2PC 强一致 低 中 低 低并发、短事务、参与者少
TCC 强一致 中 高 高 高并发、需要预留资源
Saga 最终一致 中 中高 中 长事务、多步骤流程
消息最终一致 最终一致 高 低 低 高吞吐、异步解耦、大部分业务

看到这里你应该明白了。没有银弹。选哪种方案,取决于你的业务对一致性的要求、对性能的容忍度、以及团队能承受的复杂度。

但如果你问我,我会说:能用消息最终一致性解决的,就别折腾 2PC 和 TCC 了。

分布式事务方案对比与演进 2PC 两阶段提交 TCC 补偿事务 Saga 长事务 消息最终一致性 (Outbox Pattern 核心) 演进趋势 • 2PC 强一致但性能差,逐渐被淘汰 • TCC 和 Saga 在特定场景仍有价值 • 消息最终一致性因高性能、低复杂度成为主流

这张图展示了分布式事务方案的演进脉络。从 2PC 到 TCC、Saga,再到消息最终一致性,趋势很清晰:从强一致走向最终一致,从中心化走向异步解耦。