Spring Boot 中的 TCC 事务
在分布式系统中,事务一直是一个棘手的问题。传统的 ACID 事务无法满足分布式系统的需求,因为它们需要强一致性、单点故障和网络延迟等问题。近年来,随着微服务架构的普及,TCC 事务成为了一种非常流行的分布式事务解决方案。在 Spring Boot 中,我们可以很容易地使用 TCC 事务来管理分布式事务。本文将介绍 TCC 事务的概念和原理,并说明如何在 Spring Boot 中使用它们。

TCC 事务的概念和原理
TCC 事务是一种基于补偿事务的分布式事务解决方案。它由 Try、Confirm 和 Cancel 三个阶段组成,每个阶段都是一个本地事务。
在 TCC 事务中,Try 阶段会尝试执行业务操作,并为 Confirm 和 Cancel 阶段做好准备。如果 Try 阶段执行成功,则 Confirm 阶段会提交事务,否则 Cancel 阶段会回滚事务。
举个例子,假设我们要在两个账户之间转账。在 TCC 事务中,我们可以这样实现:
- Try 阶段:从账户 A 中扣减金额,同时向账户 B 中增加金额。
 - Confirm 阶段:提交 Try 阶段的操作,将金额转移成功。
 - Cancel 阶段:回滚 Try 阶段的操作,将金额转移失败。
 
在 TCC 事务中,如果 Confirm 阶段执行成功,则整个事务就提交成功了;如果 Confirm 阶段执行失败,则整个事务就会回滚。
Spring Boot 中的 TCC 事务实现
在 Spring Boot 中,我们可以使用 Seata 来实现 TCC 事务。Seata 是一款开源的分布式事务解决方案,可以帮助我们实现分布式事务的管理和控制。
以下是一个简单的 Spring Boot + Seata TCC 事务示例,用于转账操作:
- 定义 TCC 服务接口
 
public interface AccountService {
    @TccTransaction
    void transfer(String fromAccountId, String toAccountId, BigDecimal amount);
    boolean tryTransfer(String fromAccountId, String toAccountId, BigDecimal amount);
    void confirmTransfer(String fromAccountId, String toAccountId, BigDecimal amount);
    void cancelTransfer(String fromAccountId, String toAccountId, BigDecimal amount);
}
 
在这个示例中,我们定义了一个 AccountService 接口,其中包含了 transfer、tryTransfer、confirmTransfer 和 cancelTransfer 四个方法。其中,transfer 方法是一个 TCC 事务方法,tryTransfer、confirmTransfer 和 cancelTransfer 方法是其对应的 Try、Confirm 和 Cancel 方法。
- 实现 TCC 服务接口
 
@Service
public class AccountServiceImpl implements AccountService {
    @Resource
    private AccountMapper accountMapper;
    @Override
    public boolean tryTransfer(String fromAccountId, String toAccountId, BigDecimal amount) {
        Account fromAccount = accountMapper.selectById(fromAccountId);
        if (fromAccount.getBalance().compareTo(amount) < 0) {
            throw new RuntimeException("Insufficient balance");
        }
        accountMapper.updateBalance(fromAccountId, fromAccount.getBalance().subtract(amount));
        return true;
    }
    @Override
    public void confirmTransfer(String fromAccountId, String toAccountId, BigDecimal amount) {
        Account toAccount = accountMapper.selectById(toAccountId);
        accountMapper.updateBalance(toAccountId, toAccount.getBalance().add(amount));
    }
    @Override
    public void cancelTransfer(String fromAccountId, String toAccountId, BigDecimal amount) {
        Account fromAccount = accountMapper.selectById(fromAccountId);
        accountMapper.updateBalance(fromAccountId, fromAccount.getBalance().add(amount));
    }
    @Override
    public void transfer(String fromAccountId, String toAccountId, BigDecimal amount) {
        boolean result = tryTransfer(fromAccountId, toAccountId, amount);
        if (!result) {
            throw new RuntimeException("Try transfer failed");
        }
    }
}
 
在这个示例中,我们实现了AccountService 接口,并覆盖了 tryTransfer、confirmTransfer 和 cancelTransfer 三个方法。其中,tryTransfer 方法会尝试扣减账户余额并返回 true,如果余额不足则会抛出异常;confirmTransfer 方法会向目标账户增加金额;cancelTransfer 方法会将扣减的金额恢复到原账户中。transfer 方法是 TCC 事务方法,它会在 tryTransfer 方法执行成功后提交事务,否则回滚事务。
- 配置 Seata 数据源
 
在 Spring Boot 中,我们需要配置 Seata 数据源来支持 TCC 事务。以下是一个基本的 Seata 数据源配置:
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/seata
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
  cloud:
    alibaba:
      seata:
        tx-service-group: my_test_tx_group
        config:
          type: nacos
          serverAddr: localhost:8848
          namespace: public
          config-mode: file
 
在这个示例中,我们使用了 Seata 的 Nacos 配置中心来存储配置信息。tx-service-group 属性指定了 Seata 事务组的名称,config-type 属性指定了配置中心的类型,serverAddr 属性指定了配置中心的地址,namespace 属性指定了配置中心的命名空间,config-mode 属性指定了配置中心的模式。
- 配置 Seata 代理和 TCC 事务
 
seata:
  enabled: true
  application-id: account-service
  tx-service-group: my_test_tx_group
  service:
    vgroup-mapping:
      account-service: my_test_tx_group
    group-list: my_test_tx_group
  config:
    type: nacos
    serverAddr: localhost:8848
    namespace: public
    config-mode: file
  registry:
    type: nacos
    serverAddr: localhost:8848
    namespace: public
  tm:
    commit_retry_count: 5
    rollback_retry_count: 5
  undo:
    data-validation: true
    log-table: undo_log
 
在这个示例中,我们配置了 Seata 的代理和 TCC 事务。enabled 属性指定了是否启用 Seata,application-id 属性指定了当前应用的 ID,tx-service-group 属性指定了 Seata 事务组的名称,service 属性指定了应用和事务组之间的映射关系,config 属性指定了配置中心的类型和地址,registry 属性指定了注册中心的类型和地址,tm 属性指定了事务管理器的参数,undo 属性指定了事务撤销的参数。
- 配置 Spring Boot 应用
 
最后,我们需要在 Spring Boot 应用中配置 Seata 数据源和 TCC 事务。以下是一个基本的 Spring Boot 配置文件:
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/account
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
  cloud:
    alibaba:
      seata:
        tx-service-group: my_test_tx_group
        config:
          type: nacos
          serverAddr: localhost:8848
          namespace: public
          config-mode: file
        proxy:
          table: undo_log
          log-store: db
 
在这个示例中,我们配置了 Spring Boot 应用的数据源和 Seata 的 TCC 事务代理。tx-service-group 属性指定了 Seata 事务组的名称,config 属性指定了配置中心的类型和地址,proxy 属性指定了事务代理的参数。
总结
TCC 事务是一种基于补偿事务的分布式事务解决方案,可以帮助我们解决分布式事务的问题。在 Spring Boot 中,我们可以使用 Seata 来实现 TCC 事务,并利用其强大的功能来管理和控制分布式事务。本文介绍了 TCC 事务的概念和原理,并给出了一个基本的 Spring Boot + Seata TCC 事务示例。通过学习本


















