4. 分布式事务:两阶段提交(2PC)、三阶段提交(3PC)、TCC模式在流处理中的局限与改进
聊到流处理中的数据一致性,分布式事务是个绕不开的话题。很多朋友一听到「分布式事务」就头大,觉得这东西又慢又复杂。说实话,我早年也有这感觉。但后来在金融项目中踩过几次坑,才真正理解——不是分布式事务不好用,而是你没选对场景和模式。
今天咱们就掰开揉碎,聊聊三种经典方案:2PC、3PC、TCC。它们在传统数据库事务里很成熟,但放到流处理中,问题就来了。
4.1 两阶段提交(2PC)—— 最经典,也最「重」
2PC 的逻辑其实很简单。一个协调者,多个参与者。分两步走:
- 第一阶段(投票):协调者问所有参与者:「准备好了吗?」
- 第二阶段(提交/回滚):如果所有人都说「准备好了」,协调者就通知大家提交。但凡有一个人说「不行」,就全部回滚。
听起来很完美,对吧?但我在实际项目中遇到过这么个情况:
真实案例:某支付系统的对账模块,用 2PC 协调多个数据库。结果有一次,协调者在第二阶段刚发出「提交」指令,自己挂了。部分节点收到了指令并提交,部分没收到,还在等。整个系统卡死,数据直接不一致。
这就是 2PC 最大的问题——同步阻塞。协调者挂了,参与者只能干等。而且它没有容错机制,单点故障就能让整个事务瘫痪。
我曾经踩过的坑:在流处理场景下,2PC 的「两阶段」意味着要锁住资源。对于高吞吐的 Kafka 流,每来一条消息都锁一下,性能直接崩盘。所以,流处理中几乎不用原生 2PC。
4.2 三阶段提交(3PC)—— 改进,但不够彻底
3PC 在 2PC 基础上加了一个「预提交」阶段。说白了,就是把两阶段拆成三阶段:
- CanCommit:问大家能不能执行?
- PreCommit:告诉大家准备执行,但先别提交。
- 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 的三个阶段拆成独立的 Topic,每个阶段都是异步消息。Try 阶段预占资源,Confirm 阶段真正提交,Cancel 阶段回滚。这样既保证了最终一致性,又不会阻塞主流程。
我的建议:如果你正在设计流处理系统,别一上来就上 2PC。先问问自己——我真的需要强一致吗?大多数时候,TCC + 幂等性 + 重试机制,已经足够应对金融场景了。记住,简单才是王道。