944 字
3 分钟
TCC 模式:业务层面的分布式事务方案
双十一零点,某电商平台每秒处理数万笔订单。每笔订单涉及账户扣款、库存扣减、订单创建三个服务。2PC 的同步阻塞在高并发下会拖垮整个系统,而 TCC 模式通过业务层面的资源预留和确认,避免了数据库锁的阻塞问题,成为电商场景的首选方案。
一、TCC 的核心思想
TCC 将分布式事务拆分为三个阶段:
| 阶段 | 操作 | 目标 |
|---|---|---|
| Try | 预留资源,检查可行性 | 为 Confirm/Cancel 准备 |
| Confirm | 确认执行,使用预留资源 | 真正完成业务 |
| Cancel | 取消执行,释放预留资源 | 回滚,释放资源 |
与 2PC 的关键区别:2PC 在数据库层面锁定资源,TCC 在业务层面预留资源。
2PC: Prepare → 数据库锁 → Commit/Rollback → 释放锁TCC: Try → 业务冻结 → Confirm/Cancel → 真正扣款/释放冻结以账户扣款为例:
Try: 检查余额 ≥ 100元 → 冻结 100元(可用余额不变,冻结余额增加)Confirm: 冻结余额 -100元 → 可用余额 -100元 → 扣款完成Cancel: 冻结余额 -100元 → 可用余额不变 → 释放冻结二、TCC 流程详解
2.1 正常流程
sequenceDiagram
participant TM as 事务管理器
participant A as 账户服务
participant I as 库存服务
participant O as 订单服务
TM->>A: Try(冻结100元)
TM->>I: Try(冻结1件库存)
TM->>O: Try(创建待确认订单)
A-->>TM: 成功
I-->>TM: 成功
O-->>TM: 成功
TM->>A: Confirm(扣款)
TM->>I: Confirm(扣减库存)
TM->>O: Confirm(确认订单)
2.2 Try 失败流程
sequenceDiagram
participant TM as 事务管理器
participant A as 账户服务
participant I as 库存服务
TM->>A: Try(冻结100元)
A-->>TM: 成功
TM->>I: Try(冻结库存)
I-->>TM: 失败(库存不足)
TM->>A: Cancel(释放冻结)
TM->>I: Cancel(忽略)
2.3 TCC vs 2PC 对比
| 维度 | 2PC | TCC |
|---|---|---|
| 资源锁定 | 数据库锁 | 业务层面冻结 |
| 阻塞程度 | 严重 | 较轻 |
| 业务侵入 | 无 | 高(需实现 Try/Confirm/Cancel) |
| 回滚粒度 | 粗粒度 | 细粒度 |
| 适用场景 | 短事务 | 高并发、业务可改造 |
三、TCC 的三大难题
3.1 空回滚
问题:Try 请求因网络丢失未到达参与者,但事务管理器因超时触发了 Cancel。此时参与者没有预留资源,Cancel 操作应该做什么?
解决方案:参与者记录事务状态,Cancel 时检查 Try 是否执行过:
Cancel 收到事务 ID txn-001: 查询事务日志 → txn-001 不存在 → 空回滚(直接返回成功) 同时记录空回滚标记,防止后续 Try 成功执行3.2 悬挂
问题:Cancel 请求先于 Try 请求到达参与者(网络延迟导致顺序颠倒)。Cancel 空回滚后,Try 请求才到达并成功预留资源,但永远不会被 Confirm 或 Cancel。
解决方案:Try 执行前检查是否已经空回滚:
Try 收到事务 ID txn-001: 查询事务日志 → txn-001 已标记为空回滚 → 拒绝执行 Try3.3 幂等性
问题:Confirm 或 Cancel 请求可能因网络问题重试,参与者需要保证重复执行的结果一致。
解决方案:每个 TCC 操作携带全局事务 ID,参与者记录已执行的操作:
Confirm 收到事务 ID txn-001: 查询事务日志 → txn-001 已 Confirm → 直接返回成功(幂等)四、Seata TCC 实践
Seata 是阿里开源的分布式事务框架,支持 AT、TCC、Saga 等模式。
4.1 TCC 接口定义
@LocalTCCpublic interface AccountTCCService {
@TwoPhaseBusinessAction( name = "accountTcc", commitMethod = "confirm", rollbackMethod = "cancel" ) boolean try(BusinessActionContext context, Long userId, BigDecimal amount);
boolean confirm(BusinessActionContext context);
boolean cancel(BusinessActionContext context);}4.2 Try 实现
@Overridepublic boolean try(BusinessActionContext context, Long userId, BigDecimal amount) { String txnId = context.getXid(); Account account = accountMapper.selectById(userId);
// 幂等性检查 if (isAlreadyExecuted(txnId, "TRY")) { return true; }
// 悬挂检查:是否已空回滚 if (isAlreadyRolledBack(txnId)) { return false; }
// 检查余额 if (account.getAvailableBalance().compareTo(amount) < 0) { return false; }
// 冻结金额 account.setFrozenBalance(account.getFrozenBalance().add(amount)); accountMapper.updateById(account);
// 记录事务状态 recordExecution(txnId, "TRY"); return true;}4.3 Confirm 实现
@Overridepublic boolean confirm(BusinessActionContext context) { String txnId = context.getXid();
// 幂等性检查 if (isAlreadyExecuted(txnId, "CONFIRM")) { return true; }
Long userId = context.getActionContext("userId"); BigDecimal amount = context.getActionContext("amount");
Account account = accountMapper.selectById(userId); account.setAvailableBalance(account.getAvailableBalance().subtract(amount)); account.setFrozenBalance(account.getFrozenBalance().subtract(amount)); accountMapper.updateById(account);
recordExecution(txnId, "CONFIRM"); return true;}4.4 Cancel 实现
@Overridepublic boolean cancel(BusinessActionContext context) { String txnId = context.getXid();
// 幂等性检查 if (isAlreadyExecuted(txnId, "CANCEL")) { return true; }
// 空回滚检查 if (!isTryExecuted(txnId)) { recordEmptyRollback(txnId); return true; }
Long userId = context.getActionContext("userId"); BigDecimal amount = context.getActionContext("amount");
Account account = accountMapper.selectById(userId); account.setFrozenBalance(account.getFrozenBalance().subtract(amount)); accountMapper.updateById(account);
recordExecution(txnId, "CANCEL"); return true;}五、TCC 适用场景
5.1 适合使用 TCC 的场景
- 业务逻辑可以拆分为 Try/Confirm/Cancel 三步
- 对一致性要求较高(如金融、电商核心交易)
- 高并发场景,不能接受数据库锁阻塞
- 参与者数量可控(2-5 个服务)
5.2 不适合使用 TCC 的场景
- 业务逻辑无法拆分(如纯查询操作)
- 参与者数量过多(协调成本高)
- 对实时性要求不高的场景(更适合 Saga)
- 团队无法承担 TCC 的开发成本
六、TCC 与其他分布式事务方案的对比
| 维度 | 2PC | TCC | Saga |
|---|---|---|---|
| 一致性 | 强一致 | 最终一致 | 最终一致 |
| 阻塞 | 严重 | 无 | 无 |
| 业务侵入 | 无 | 高 | 中 |
| 隔离性 | 强 | 中 | 弱 |
| 实现难度 | 低 | 高 | 中 |
| 适用场景 | 短事务 | 高并发核心交易 | 长流程 |
TCC 模式通过业务层面的资源预留避免了数据库锁阻塞,是高并发场景下分布式事务的首选方案。但 TCC 的业务侵入性高,需要为每个操作实现三个方法,并处理空回滚、悬挂和幂等性问题。在业务可改造且对一致性要求较高的场景下,TCC 是值得投入的方案。
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
TCC 模式:业务层面的分布式事务方案
https://blog.souloss.com/posts/distributed/distributed-tcc-pattern/ 部分信息可能已经过时
相关文章 智能推荐
1
Saga 模式:长事务的编排方案
分布式系统深入 深入解析 Saga 模式——编排式与协调式的差异、补偿操作设计、隔离性问题与生产实践
2
2PC 与 3PC:分布式事务的协议基础
分布式系统深入 深入解析两阶段提交与三阶段提交协议——原理、阻塞问题与工程实践中的改进方案
3
CAP 定理:分布式系统的不可能三角
分布式系统深入 深入理解 CAP 定理——一致性、可用性与分区容错性为何不可兼得,以及工程实践中的取舍策略
4
Paxos 协议:分布式共识的理论基石
分布式系统深入 深入理解 Paxos 协议——Basic Paxos 与 Multi-Paxos 的原理、推导过程与工程挑战
5
分布式追踪:定位跨服务性能瓶颈
分布式系统深入 深入解析分布式追踪——OpenTelemetry 标准、Span 与 Trace 模型、采样策略与 Jaeger 工程实践






