4. 分布式事务:2PC、3PC、TCC、Saga模式
分布式事务,说白了就是让一堆独立的数据库或服务,像在同一个数据库里那样保持数据一致。听起来很美好,但做起来坑不少。我这些年踩过的坑,今天一次性给你讲清楚。
核心观点:没有银弹。2PC、3PC、TCC、Saga 各有各的适用场景,选错了就是给自己挖坑。
4.1 两阶段提交(2PC)—— 最经典的“协调者”模式
2PC 是最早的分布式事务方案。它引入了一个协调者(Coordinator),所有参与者(Participant)都听它的。
第一阶段:准备阶段(Vote)
协调者问所有参与者:“你们能提交吗?”
参与者回答:“能”(Yes)或“不能”(No)。
第二阶段:提交阶段(Commit)
如果所有人都说 Yes,协调者说:“提交!”
如果有人说了 No,协调者说:“回滚!”
听起来很简单对吧?但我在项目中遇到过一个问题:协调者挂了怎么办?参与者一直等着协调者的指令,资源锁着不放,整个系统就卡死了。
避坑指南:我曾经在一个金融项目中用 2PC 做跨行转账,结果协调者节点宕机,所有参与者都处于“预提交”状态,锁了 20 分钟。从那以后,我坚决不在高并发场景下用 2PC。
| 特性 | 说明 |
|---|---|
| 优点 | 强一致性,实现简单 |
| 缺点 | 同步阻塞,协调者单点故障,数据不一致风险 |
| 适用场景 | 低并发、短事务、对一致性要求极高的场景 |
4.2 三阶段提交(3PC)—— 2PC 的“改良版”
3PC 在 2PC 的基础上加了一个 CanCommit 阶段,并且引入了超时机制。说白了就是:先问一遍“能不能干”,再问一遍“准备好了没”,最后才说“干”。
三个阶段:
- CanCommit:协调者问:“你们能参与吗?”(只是问问,不锁资源)
- PreCommit:协调者说:“准备干活!”(参与者开始锁资源)
- DoCommit:协调者说:“提交!”(或者“回滚!”)
3PC 解决了 2PC 的阻塞问题吗?并没有完全解决。 我个人的经验是:3PC 在网络分区的情况下,仍然可能出现数据不一致。而且多了一个阶段,性能更差了。
我的建议:除非你是在做学术研究,否则别碰 3PC。实际项目中,2PC 和 TCC 已经覆盖了大部分场景。
4.3 TCC(Try-Confirm-Cancel)—— 业务层面的“补偿”
TCC 是另一种思路:把事务拆成三个业务操作。
- Try:预留资源(比如冻结账户里的钱)
- Confirm:确认执行(真正扣钱)
- Cancel:取消执行(解冻账户里的钱)
你想想看,TCC 的好处是什么?不锁数据库资源。 每个阶段都是业务操作,不是数据库的 XA 事务。所以性能比 2PC 好很多。
但 TCC 也有坑:你得自己写 Try、Confirm、Cancel 三个方法。 而且 Cancel 方法必须能正确处理 Try 阶段的各种情况。我曾经在一个订单系统中,因为 Cancel 方法没处理好幂等性,导致重复退款……嗯,那天的线上事故复盘会开了三个小时。
关键点:TCC 的 Cancel 方法必须支持幂等。因为网络超时可能导致 Cancel 被多次调用。
4.4 Saga 模式 —— 长事务的“救星”
Saga 模式适合处理 长事务。它把一个大的分布式事务拆成一系列本地事务,每个本地事务都有一个对应的补偿操作。
两种实现方式:
- Choreography(编排):每个服务完成后,发消息触发下一个服务。没有中心协调者。
- Orchestration(编排):有一个协调者(Orchestrator)来告诉每个服务该做什么。
我个人习惯用 Orchestration 方式。为什么?因为 Choreography 方式虽然解耦,但出了问题很难排查。你想想看,一个事务跨了 5 个服务,每个服务都发消息,消息顺序乱了怎么办?
| 特性 | Choreography | Orchestration |
|---|---|---|
| 耦合度 | 低 | 高 |
| 可追踪性 | 差 | 好 |
| 复杂度 | 高 | 低 |
| 适用场景 | 简单流程 | 复杂流程 |
4.5 四种模式对比
我把这四种模式放在一起对比一下,方便你选型:
| 模式 | 一致性 | 性能 | 复杂度 | 适用场景 |
|---|---|---|---|---|
| 2PC | 强一致性 | 低 | 低 | 短事务、低并发 |
| 3PC | 弱一致性 | 更低 | 中 | 几乎不用 |
| TCC | 最终一致性 | 高 | 高 | 高并发、短事务 |
| Saga | 最终一致性 | 中 | 高 | 长事务、跨服务 |
重要提醒:不要为了用分布式事务而用分布式事务。很多场景下,最终一致性 + 补偿机制 就足够了。过度设计是分布式系统的头号敌人。
4.6 核心流程对比图
下面这张图展示了四种模式的核心流程差异,我建议你仔细看看:
4.7 我的实战建议
说了这么多,最后给你几条实在的建议:
- 能用本地事务解决的,别用分布式事务。 很多场景下,把数据拆分到同一个数据库里,比搞分布式事务简单得多。
- 优先考虑 TCC 和 Saga。 2PC 的阻塞问题太致命,3PC 又没解决根本问题。
- 做好补偿机制。 不管用哪种模式,都要假设事务会失败。补偿代码要写得和主流程一样健壮。
- 监控和告警不能少。 分布式事务失败后,如果没有及时发现,数据不一致会越积越多。
最后说一句:分布式事务没有完美的方案。选型的时候,想清楚你的业务到底需要什么级别的数据一致性。很多时候,“差不多一致” 比 “完全一致但系统挂了” 要好得多。