mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
944 字
3 分钟
TCC 模式:业务层面的分布式事务方案
2025-02-11

双十一零点,某电商平台每秒处理数万笔订单。每笔订单涉及账户扣款、库存扣减、订单创建三个服务。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 对比#

维度2PCTCC
资源锁定数据库锁业务层面冻结
阻塞程度严重较轻
业务侵入高(需实现 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 已标记为空回滚 → 拒绝执行 Try

3.3 幂等性#

问题:Confirm 或 Cancel 请求可能因网络问题重试,参与者需要保证重复执行的结果一致。

解决方案:每个 TCC 操作携带全局事务 ID,参与者记录已执行的操作:

Confirm 收到事务 ID txn-001:
查询事务日志 → txn-001 已 Confirm → 直接返回成功(幂等)

四、Seata TCC 实践#

Seata 是阿里开源的分布式事务框架,支持 AT、TCC、Saga 等模式。

4.1 TCC 接口定义#

@LocalTCC
public 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 实现#

@Override
public 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 实现#

@Override
public 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 实现#

@Override
public 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 与其他分布式事务方案的对比#

维度2PCTCCSaga
一致性强一致最终一致最终一致
阻塞严重
业务侵入
隔离性
实现难度
适用场景短事务高并发核心交易长流程

TCC 模式通过业务层面的资源预留避免了数据库锁阻塞,是高并发场景下分布式事务的首选方案。但 TCC 的业务侵入性高,需要为每个操作实现三个方法,并处理空回滚、悬挂和幂等性问题。在业务可改造且对一致性要求较高的场景下,TCC 是值得投入的方案。

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

TCC 模式:业务层面的分布式事务方案
https://blog.souloss.com/posts/distributed/distributed-tcc-pattern/
作者
Souloss
发布于
2025-02-11
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时