java中实现tcc分布式事务需分try-confirm-cancel三阶段,各阶段接口须幂等可重试,并处理空回滚、悬挂等异常;推荐使用seata等成熟框架,避免手写协调器。

Java 中实现基于 TCC(Try-Confirm-Cancel)模式的分布式事务,核心在于将业务逻辑拆分为三个明确阶段,并由框架协调各服务的执行与回滚。它不依赖数据库事务,而是通过应用层控制一致性,适合高并发、跨异构系统(如微服务+外部 API)的场景。
一、TCC 三阶段职责必须清晰分离
每个参与事务的服务需提供三个幂等接口:
- Try 阶段:做业务检查和资源预留(如冻结账户余额、预占库存),不真正扣减;需保证幂等与可重试,失败即中止整个事务。
- Confirm 阶段:执行实际操作(如扣款、出库),仅在 Try 成功后调用;必须可重试,且不检查前置状态(假设 Try 已成功)。
- Cancel 阶段:释放 Try 预留的资源(如解冻余额、归还库存),在 Try 失败或 Confirm 失败时触发;同样需幂等、可重试。
注意:Confirm 和 Cancel 必须设计为“空回滚”“悬挂”“空提交”等异常情况的安全处理——例如 Try 未执行但 Cancel 被调用,需识别并忽略;Try 执行后超时未收到 Confirm 指令,后续 Confirm 到达仍要正确处理。
二、选用成熟框架降低实现成本
不建议从零手写 TCC 协调器。推荐以下 Java 生态方案:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- Seata AT 模式 + 自定义 TCC 模块:Seata 原生支持 TCC 模式。只需实现 `TwoPhaseBusinessAction` 注解接口,在 Service 方法上标注 `@TwoPhaseBusinessAction` 并指定 `commitMethod` 和 `rollbackMethod`,框架自动管理事务上下文传播与重试。
- ServiceComb Pack(已归档,但设计思想可用):基于 Saga + TCC 混合模型,适合遗留系统集成。
- 自研轻量协调器(小规模场景):用 Redis 记录事务 ID 与各分支状态,结合 Spring AOP 拦截方法调用,通过注解(如 `@TccTransaction`)触发 Try → 异步调度 Confirm/Cancel。关键点是事务日志持久化(建议存 MySQL 或 MongoDB)和后台补偿任务(Quartz 或 XXL-JOB 定期扫描超时事务)。
三、关键代码结构示例(以 Seata TCC 为例)
一个账户扣款服务的 TCC 实现片段:
@Service
public class AccountTccService {
@GlobalTransactional // 启动全局事务(Seata)
public void transfer(String fromId, String toId, BigDecimal amount) {
accountTccAction.prepareTransfer(fromId, toId, amount); // Try
}
@TwoPhaseBusinessAction(name = "prepareTransfer", commitMethod = "commitTransfer", rollbackMethod = "cancelTransfer")
public boolean prepareTransfer(BusinessActionContext actionContext,
@BusinessActionContextParameter(paramName = "fromId") String fromId,
@BusinessActionContextParameter(paramName = "toId") String toId,
@BusinessActionContextParameter(paramName = "amount") BigDecimal amount) {
// Try:检查余额,冻结金额(更新 frozen_balance 字段)
return accountMapper.tryDeduct(fromId, amount);
}
public boolean commitTransfer(BusinessActionContext actionContext) {
String fromId = (String) actionContext.getActionContext("fromId");
BigDecimal amount = (BigDecimal) actionContext.getActionContext("amount");
// Confirm:真正扣减 balance,清空 frozen_balance
return accountMapper.confirmDeduct(fromId, amount);
}
public boolean cancelTransfer(BusinessActionContext actionContext) {
String fromId = (String) actionContext.getActionContext("fromId");
BigDecimal amount = (BigDecimal) actionContext.getActionContext("amount");
// Cancel:解冻 frozen_balance
return accountMapper.cancelDeduct(fromId, amount);
}
}
注意:所有方法必须返回 boolean 表示执行结果;Confirm/Cancel 方法参数只能是 `BusinessActionContext` 或带 `@BusinessActionContextParameter` 的简单类型;数据库表需增加 `frozen_balance` 等预留字段支撑 Try 阶段。
四、生产环境必须考虑的细节
TCC 不是银弹,落地时需重点关注:
- 幂等性强制保障:Confirm/Cancel 接口开头加唯一事务 ID + 分支 ID 的去重表(或 Redis SETNX)判断是否已执行过。
- 空回滚防护:Cancel 方法中先查 Try 是否执行(比如查 `tcc_try_log` 表),若无记录则直接返回 true(避免误释放未预留的资源)。
- 悬挂处理:Try 成功后长时间未收到 Confirm/Cancle 指令,需靠定时任务扫描“已 Try 未终态”的事务,主动触发 Cancel(防止资源长期被冻结)。
- 日志与可观测性:记录每笔事务的 Try/Confirm/Cancel 时间、入参、结果、异常堆栈;接入 SkyWalking 或 Prometheus 监控 TCC 调用成功率、平均耗时、补偿次数。
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










