TCC系统的实现方法
大家好,我是程序员老王。今天在面试鸭返利网上看到很多朋友在讨论分布式事务解决方案,尤其被问到 TCC系统 的实现细节。正好结合我实际项目经验,和大家聊聊 TCC系统 的核心思路,绝对能帮你面试加分!
2025年Java面试宝典(含分布式事务专题)已整理: 点我领取,提取码:9b3g
一、 什么是TCC系统?面试官到底想问什么?
面试官抛出“TCC系统怎么实现”时,其实在考察:
- 你是否理解分布式事务的痛点(CAP、数据不一致);
- 你是否掌握TCC这种主流补偿型方案的思想;
- 能否清晰拆解Try-Confirm-Cancel三个阶段;
- 是否考虑过TCC系统的容错和幂等性。
TCC(Try-Confirm-Cancel)本质是将一个完整业务逻辑拆成三个动作:
- Try:预操作,锁定/预留资源(如冻结余额、占库存)。
- Confirm:真正提交,使用预留资源(如扣款、减库存)。
- Cancel:回滚补偿,释放预留资源(如解冻余额、释放库存)。
(TCC事务流程示意:Try -> Confirm/Cancel)
二、 TCC系统实现的核心步骤拆解
步骤1:业务分析,定义Try/Confirm/Cancel接口
- 关键点: 每个参与事务的服务(如订单服务、账户服务、库存服务)都要提供这三个接口。
- 举例(账户服务):
tryReduceBalance(userId, amount):冻结用户部分余额。confirmReduceBalance(userId, amount):实际扣减已冻结的余额。cancelReduceBalance(userId, amount):解冻冻结的余额。
步骤2:实现Try阶段 - 资源预留
- 核心: Try操作必须是幂等的,且能预留资源。
- 技术实现:
- 在业务表中添加
冻结状态字段(如balance_frozen)。 tryReduceBalance逻辑:- 检查账户可用余额
>= amount。 - 将
amount从可用余额转移到冻结余额字段。更新成功后返回成功。
- 检查账户可用余额
- 关键点: 这个阶段只冻结,不真正消费。数据状态变化在本地数据库事务内完成。
- 在业务表中添加
步骤3:实现Confirm阶段 - 最终提交
- 核心: Confirm操作必须是幂等的且业务不可逆。它消费Try预留的资源。
- 技术实现:
confirmReduceBalance逻辑:- 检查是否存在对应的、未完成的冻结记录(
frozen_amount)。 - 将冻结金额清零(或转移到已消费状态),真实扣减总余额。
- 检查是否存在对应的、未完成的冻结记录(
- 关键点: Confirm操作理论上必须成功(因为Try已预留资源),所以设计要简单高效。如果失败,事务协调器会重试。
步骤4:实现Cancel阶段 - 优雅回滚
- 核心: Cancel操作必须是幂等的,用于释放Try阶段预留的资源。
- 技术实现:
cancelReduceBalance逻辑:- 检查是否存在对应的、未完成的冻结记录(
frozen_amount)。 - 将冻结金额
amount加回到可用余额中,并清零冻结金额。
- 检查是否存在对应的、未完成的冻结记录(
- 关键点: 这是补偿操作,让系统恢复到Try之前的状态。
步骤5:事务协调器是关键
- 职责: 驱动整个TCC系统流程,按顺序调用各服务的Try,然后根据Try结果决定全局提交(Confirm)或全局回滚(Cancel)。
- 实现方式(常用):
- 独立服务: 单独部署一个事务协调器服务,维护事务日志(状态机)。
- 状态存储: 使用数据库或Redis记录事务ID、状态(INIT->TRYING->CONFIRMING/CANCELLING)、参与者信息。
- 状态驱动: 协调器根据事务状态,调用相应的参与者接口(Try/Confirm/Cancel)。
- 定时补偿: 增加定时任务扫描“悬挂事务”(如TRY成功但迟迟未收到Confirm/Cancel指令),根据最终状态进行补发。
(事务协调器驱动流程示例)
三、 TCC系统必须解决的难点
-
幂等性: 重中之重!网络超时、协调器重试都可能导致重复调用。每个Try/Confirm/Cancel接口都要保证多次执行效果和一次相同。常用方法:
- 唯一事务ID: 每个分布式事务分配全局唯一ID。
- 状态机: 每个参与者记录每个事务ID的执行状态(如:
try_success,confirmed)。 - 前置检查: 接口执行前,先查库判断该事务ID是否已处理过当前阶段。
-
空回滚: Cancel可能在Try还没执行时就被调用(比如Try请求超时,协调器触发回滚)。
- 解决: 在
cancelXXX接口中,如果找不到对应的冻结记录(说明Try未执行或已回滚),仍需记录一条回滚日志,并返回成功(幂等)。
- 解决: 在
-
悬挂: Try在Cancel执行之后才到达。
- 解决: 在
tryXXX接口中,检查是否已有对应事务ID的Cancel记录。如果有,则拒绝执行Try或直接执行Cancel逻辑。
- 解决: 在
-
最终一致性: TCC系统保证的是最终一致性。Confirm/Cancel可能延迟,业务上要能接受短暂中间状态(如余额冻结中)。
-
隔离性: Try阶段的资源预留(如冻结)提供了业务层面的弱隔离,防止脏读(如看到冻结金额但认为已扣款)。
四、 TCC系统的优缺点和适用场景
- 优点:
- 性能较高(Confirm/Cancel通常很快)。
- 数据最终一致性强。
- 能较好解决跨服务的复杂业务事务。
- 缺点:
- 实现复杂,侵入业务(每个服务要改三个接口)。
- 需要保证幂等、处理空回滚/悬挂。
- 开发成本高。
- 适用场景: 对一致性要求高、性能要求高、且能接受一定开发复杂性的场景。经典例子:电商下单(扣库存、减优惠券、扣余额)。
五、 面试如何答好TCC系统问题?
- 思路清晰: 分阶段(Try, Confirm, Cancel)讲作用、设计要点(幂等、预留资源)。
- 难点突出: 主动提及空回滚、悬挂、幂等的解决方案(事务ID+状态机)。
- 结合业务: 举个实际例子说明(如转账、下单)。
- 对比方案: 提一下和2PC、Saga的区别(TCC是补偿型,2PC是阻塞型)。
- 实践踩坑: 如果实际做过,提一下协调器选型(如用开源框架Seata)和遇到的坑。
最后给大家一个小福利:如果大家需要购买面试鸭会员,可以通过 面试鸭返利网 (mianshiyafanli.com) 找到我,返利25元! 省下的钱买杯咖啡提提神,继续刷题!
TCC系统是分布式架构下的硬骨头,理解其原理和实现难点,面试时就能游刃有余。希望这篇分享对你有帮助!建议大家结合我开头分享的网盘资料深入学习,里面有很多实战案例。



