4. 分布式事务:两阶段提交(2PC)、三阶段提交(3PC)、TCC模式在流处理中的局限与改进

聊到流处理中的数据一致性,分布式事务是个绕不开的话题。很多朋友一听到「分布式事务」就头大,觉得这东西又慢又复杂。说实话,我早年也有这感觉。但后来在金融项目中踩过几次坑,才真正理解——不是分布式事务不好用,而是你没选对场景和模式

今天咱们就掰开揉碎,聊聊三种经典方案:2PC、3PC、TCC。它们在传统数据库事务里很成熟,但放到流处理中,问题就来了。

4.1 两阶段提交(2PC)—— 最经典,也最「重」

2PC 的逻辑其实很简单。一个协调者,多个参与者。分两步走:

  • 第一阶段(投票):协调者问所有参与者:「准备好了吗?」
  • 第二阶段(提交/回滚):如果所有人都说「准备好了」,协调者就通知大家提交。但凡有一个人说「不行」,就全部回滚。

听起来很完美,对吧?但我在实际项目中遇到过这么个情况:

真实案例:某支付系统的对账模块,用 2PC 协调多个数据库。结果有一次,协调者在第二阶段刚发出「提交」指令,自己挂了。部分节点收到了指令并提交,部分没收到,还在等。整个系统卡死,数据直接不一致。

这就是 2PC 最大的问题——同步阻塞。协调者挂了,参与者只能干等。而且它没有容错机制,单点故障就能让整个事务瘫痪。

我曾经踩过的坑:在流处理场景下,2PC 的「两阶段」意味着要锁住资源。对于高吞吐的 Kafka 流,每来一条消息都锁一下,性能直接崩盘。所以,流处理中几乎不用原生 2PC

4.2 三阶段提交(3PC)—— 改进,但不够彻底

3PC 在 2PC 基础上加了一个「预提交」阶段。说白了,就是把两阶段拆成三阶段:

  1. CanCommit:问大家能不能执行?
  2. PreCommit:告诉大家准备执行,但先别提交。
  3. DoCommit:正式提交。

这样做的好处是,引入了超时机制。如果协调者挂了,参与者超时后可以自行决定提交或放弃。这解决了 2PC 的「死等」问题。

但说实话,3PC 在流处理中依然不实用。为什么?

  • 网络开销更大,多一轮交互。
  • 参与者自行决策时,依然可能产生数据不一致。

我个人习惯:3PC 更适合对一致性要求极高、但网络环境稳定的场景。比如金融核心账务系统。但在流处理这种「高吞吐、低延迟」的领域,我建议直接跳过它。

4.3 TCC模式 —— 流处理中的「实用派」

TCC 全称是 Try-Confirm-Cancel。它不像 2PC 那样锁资源,而是通过业务逻辑来保证一致性。

三个步骤:

  • Try:预留资源。比如扣款时,先冻结金额,不真正扣走。
  • Confirm:确认执行。把冻结的金额真正扣掉。
  • Cancel:取消。把冻结的金额解冻。

你想想看,这其实特别适合流处理。因为流处理天然就是「异步 + 事件驱动」的。TCC 的每个阶段都可以看作一个独立的事件,互不阻塞。

我在项目中用过的改进方案:在 Kafka 流中,我们把 TCC 的 Try 阶段做成一个「预占消息」,Confirm 阶段通过另一个 Topic 触发。如果 Confirm 失败,就靠重试机制 + 幂等性来兜底。效果非常好,吞吐量比 2PC 高了 10 倍不止。

4.4 三种模式对比

特性 2PC 3PC TCC
一致性级别 强一致 强一致(有风险) 最终一致
阻塞情况 同步阻塞 超时后可自行决策 无阻塞
容错能力 弱(协调者单点) 中等 强(可重试)
流处理适用性 极差 较好
实现复杂度 高(需业务补偿)

4.5 流处理中的改进方向

既然原生方案都不完美,那我们在流处理中怎么用?我总结了几条实战经验:

  • 放弃强一致,拥抱最终一致:流处理追求的是高吞吐,强一致会拖死系统。用 TCC + 重试 + 幂等性,基本能覆盖 99.9% 的场景。
  • 引入事务性消息:比如 Kafka 的事务 API,可以保证「生产消息」和「提交事务」原子化。这其实就是 2PC 的变种,但 Kafka 做了优化,性能好很多。
  • 状态机 + 补偿:把每个流处理步骤定义成状态,失败时走补偿逻辑。我做过一个风控系统,就是用状态机管理 TCC 的各个阶段,出错后自动回滚。

注意:TCC 的 Cancel 逻辑一定要幂等。我曾经因为 Cancel 没写好,导致同一笔退款被重复执行了三次。嗯,那天的对账报表简直没法看。

4.6 核心逻辑流程图

下面这张图,是我自己画的一个 TCC 在流处理中的改进架构。你可以看到,每个阶段都是异步的,通过消息队列解耦。

TCC 在流处理中的改进架构 Kafka 消息源 Try 阶段 预占 Topic 消息流入 预留资源 Confirm 阶段 确认 Topic 异步触发 提交资源 Cancel 阶段 取消 Topic 失败回滚 释放资源 最终一致 汇聚

这张图的核心思路就是:把 TCC 的三个阶段拆成独立的 Topic,每个阶段都是异步消息。Try 阶段预占资源,Confirm 阶段真正提交,Cancel 阶段回滚。这样既保证了最终一致性,又不会阻塞主流程。

我的建议:如果你正在设计流处理系统,别一上来就上 2PC。先问问自己——我真的需要强一致吗?大多数时候,TCC + 幂等性 + 重试机制,已经足够应对金融场景了。记住,简单才是王道

专注资料整理