SpringCloud Seata 四大模式(AT/TCC/SAGA/XA)全解析

在 Spring Cloud 微服务架构中,业务系统被拆分为多个独立部署的微服务,每个服务对应独立的数据库实例,原本单体应用内的本地事务,彻底演变为跨服务、跨数据源的分布式事务场景。最典型的电商下单场景:用户下单需依次执行「扣减商品库存」「扣减用户余额」「生成订单记录」三个操作,这三个操作分属库存服务、账户服务、订单服务,对应三个独立数据库,任何一个环节失败,都需要所有已执行的操作全部回滚,否则会出现数据不一致,这就是分布式事务要解决的核心问题。

分布式事务的核心目标,是保证跨服务、跨数据源的多个操作「要么全部成功,要么全部失败」,即满足事务的原子性。而 Spring Cloud 生态中,Seata(Simple Extensible Autonomous Transaction Architecture)作为阿里开源的轻量级分布式事务框架,凭借低侵入、高性能、易接入、多模式适配的特性,成为解决分布式事务的首选方案。

Seata 无缝集成 Spring Cloud、Spring Boot,提供了 AT、TCC、SAGA、XA 四种事务模式,分别适配不同的业务场景,从简单的常规业务到复杂的长事务、金融级强一致业务,都能完美覆盖。

一、前置认知:Spring Cloud 分布式事务痛点与 Seata 核心定位

1.1 Spring Cloud 分布式事务的核心痛点

Spring Cloud 微服务中,服务间通过 OpenFeign、Dubbo 等方式调用,每个服务独立管理自己的数据库,分布式事务的痛点主要集中在 3 点,也是我们选择 Seata 的核心原因:

  • 数据一致性难以保证:服务间调用存在网络超时、断连等问题,可能出现"部分操作成功、部分失败"(如订单生成成功,但库存未扣减),导致数据不一致;

  • 本地事务无法全局回滚:每个服务的操作都由本地事务管理,一个服务的本地事务提交后,无法被其他服务的失败操作触发回滚;

  • 开发与维护成本高:手动实现分布式事务(如基于消息队列的最终一致性),需编写大量补偿代码,适配复杂场景,开发效率低、维护难度大。

1.2 Seata 核心定位与 Spring Cloud 集成优势

Seata 是一款专为微服务设计的分布式事务解决方案,核心定位是「轻量级、无侵入、多模式适配」,与 Spring Cloud 生态无缝集成,无需修改大量业务代码,就能快速实现分布式事务控制,其核心优势如下:

  • 无缝集成 Spring Cloud:支持 Spring Cloud Alibaba、Spring Cloud Netflix 等主流 Spring Cloud 生态,适配 Nacos、Eureka 等注册中心,配置简单、接入成本低;

  • 多模式适配:提供 AT、TCC、SAGA、XA 四种事务模式,覆盖 99% 的 Spring Cloud 分布式场景;

  • 低侵入性:AT、XA 模式几乎无代码侵入,只需添加注解和简单配置,就能实现分布式事务;TCC、SAGA 模式虽需编写补偿逻辑,但有明确的规范和框架支持;

  • 高性能:AT 模式基于本地事务+undo log 实现,性能接近本地事务,远超传统的 2PC 方案;

  • 易运维:提供 Seata Server 作为事务协调中心,支持集群部署,可通过控制台监控事务状态、排查问题。

1.3 Seata 核心架构(Spring Cloud 集成场景)

Seata 遵循「TC、TM、RM」三大核心角色设计,这三个角色协同工作,实现分布式事务的全局协调,在 Spring Cloud 微服务中,角色分工清晰,无需手动实现协调逻辑:

  1. TM(Transaction Manager,事务管理器) : - 部署位置:Spring Cloud 微服务中的「发起分布式事务的服务」(如订单服务,发起下单、扣库存、扣余额的全局事务); - 核心职责:通过 @GlobalTransactional 注解开启全局事务,向 TC 注册全局事务,发起全局提交或回滚请求。

  2. RM(Resource Manager,资源管理器): - 部署位置:Spring Cloud 微服务中的「参与分布式事务的所有服务」(如订单服务、库存服务、账户服务); - 核心职责:管理本地数据库资源,向 TC 注册分支事务,执行本地事务,并响应 TC 的提交/回滚指令。

  3. TC(Transaction Coordinator,事务协调器): - 部署位置:独立部署的 Seata Server 服务(可集群部署,保证高可用); - 核心职责:接收 TM 的全局事务请求,协调所有 RM 的分支事务,记录事务状态,最终决定全局事务提交或回滚,是 Seata 分布式事务的"大脑"。

Spring Cloud 集成 Seata 后,事务执行流程简化为 3 步:① TM 开启全局事务,向 TC 注册;② 各 RM 执行本地事务,向 TC 注册分支事务;③ 所有分支事务执行完成后,TM 向 TC 发起提交/回滚请求,TC 协调所有 RM 完成全局提交/回滚。

1.4 Spring Cloud 集成 Seata 前置准备(通用步骤)

无论使用 Seata 的哪种事务模式,Spring Cloud 微服务集成 Seata 的前置准备步骤一致,主要包括「部署 Seata Server」「客户端配置」两步,以下是基于 Spring Cloud Alibaba + Nacos 的完整准备流程(最主流方案):

1.4.1 部署 Seata Server(事务协调器)
  • 下载 Seata Server 安装包(推荐稳定版本 2.x),解压后修改配置文件 conf/application.yml,配置 Nacos 注册中心和数据存储模式(生产环境推荐 DB 模式,避免内存模式丢失数据):
XML 复制代码
 server:
  port: 8091 # Seata Server 默认端口
spring:
  application:
    name: seata-server
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # Nacos 地址
        group: SEATA_GROUP # 注册分组,默认即可
        namespace: public # Nacos 命名空间
# 数据存储配置(DB模式,需提前创建seata数据库)
store:
  mode: db # 存储模式:db(数据库)、file(文件,测试用)、redis(Redis)
  db:
    datasource: druid
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    min-conn: 5
    max-conn: 100
    global-table: global_table # 全局事务表
    branch-table: branch_table # 分支事务表
    lock-table: lock_table # 锁表
    distributed-lock-table: distributed_lock # 分布式锁表
  • 在 MySQL 中创建 seata 数据库,并执行 Seata 官方提供的建表语句(全局事务表、分支事务表等),建表语句可在 Seata 安装包的 conf/db_undo_log.sql 中获取;

  • 启动 Seata Server:执行解压目录下的**bin/seata-server.sh** (Linux/Mac)或 bin/seata-server.bat(Windows),启动后可在 Nacos 控制台看到 seata-server 实例。

1.4.2 Spring Cloud 客户端配置(所有参与事务的微服务)
  • 引入依赖(Spring Cloud Alibaba 环境,对应 Seata 版本):
XML 复制代码
  <!-- Spring Cloud Alibaba Seata 依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <version>2.2.10.RELEASE</version>
</dependency>
<!-- Seata 数据源代理依赖(AT/XA模式必需) -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>
  • 配置 application.yml,核心配置 Seata 注册中心、事务组、数据源代理(AT/XA 模式必需):
XML 复制代码
  spring:
  cloud:
    alibaba:
      seata:
        tx-service-group: my_seata_group # 事务组名称,所有参与事务的服务必须一致
        service:
          vgroup-mapping:
            my_seata_group: default # 事务组与Seata Server集群的映射关系
          grouplist:
            default: localhost:8091 # Seata Server 地址(集群用逗号分隔)
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/order_db?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456

# Seata 配置(AT/XA模式需配置数据源代理)
seata:
  data-source-proxy-mode: AT # 数据源代理模式,AT/XA模式需指定,TCC/SAGA模式无需配置
  registry:
    type: nacos # 注册中心类型
    nacos:
      server-addr: localhost:8848
      group: SEATA_GROUP
      namespace: public
  config:
    type: nacos # 配置中心类型
    nacos:
      server-addr: localhost:8848
      group: SEATA_GROUP
      namespace: public
  • 配置数据源代理(AT/XA 模式必需):Seata 需要通过数据源代理拦截 SQL 执行,生成 undo log(回滚日志),因此需创建数据源代理配置类:
java 复制代码
import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
public class SeataDataSourceConfig {

    // 配置数据源代理(Seata 拦截 SQL 执行)
    @Bean
    public DataSource dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy((DruidDataSource) dataSource);
    }

    // 配置事务管理器
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSourceProxy) {
        return new DataSourceTransactionManager(dataSourceProxy);
    }
}

前置准备完成后,即可根据业务场景,选择 Seata 的四种事务模式之一,实现分布式事务控制。以下将逐一拆解每种模式的原理、Spring Cloud 实操、优缺点及适用场景。

二、Seata AT 模式(首选!Spring Cloud 常规业务首选)

AT 模式是 Seata 最常用、最推荐的模式,也是 Spring Cloud 微服务常规业务的首选方案。它基于「本地事务+undo log 回滚日志」实现,无需编写补偿逻辑,几乎无代码侵入,性能接近本地事务,完美适配大多数常规业务场景(如电商下单、支付回调等),覆盖 80% 以上的分布式事务需求。

2.1 AT 模式核心原理(两阶段提交,自动补偿)

AT 模式采用「两阶段提交」机制,核心是"一阶段执行本地事务并记录回滚日志,二阶段根据全局事务状态,自动执行提交或回滚",全程无需手动编写补偿逻辑,Seata 自动完成回滚操作,原理拆解如下:

第一阶段(执行本地事务)
  1. TM 开启全局事务(添加 @GlobalTransactional 注解),向 TC 注册全局事务,获取全局事务 ID(XID);

  2. TM 调用其他微服务(如订单服务调用库存服务、账户服务),XID 会通过服务调用传递到各个 RM;

  3. 每个 RM 接收到请求后,执行本地事务(如扣减库存、扣减余额),同时 Seata 数据源代理拦截 SQL 执行,记录「undo log」(回滚日志)------ 即事务执行前的数据快照(如库存扣减前的数量、余额扣减前的金额);

  4. 本地事务提交,释放数据库锁(这是 AT 模式高性能的核心,避免长期锁表);

  5. RM 向 TC 汇报分支事务执行状态(成功/失败)。

第二阶段(全局提交/回滚)
  • 全局提交:若所有分支事务都执行成功,TC 向所有 RM 发送提交指令,RM 收到指令后,删除之前记录的 undo log(无需回滚,清理日志),全局事务完成;

  • 全局回滚:若有任意一个分支事务失败,TC 向所有 RM 发送回滚指令,RM 收到指令后,根据 undo log 中的数据快照,执行回滚操作(将数据恢复到事务执行前的状态),全局事务回滚完成。

关键说明:AT 模式的回滚是「自动补偿」,无需开发者编写任何回滚代码,Seata 通过 undo log 自动完成数据恢复,这也是其低侵入性的核心原因。同时,由于一阶段本地事务已提交、释放锁,AT 模式的性能远优于传统 2PC 方案。

2.2 Spring Cloud + Seata AT 模式实操(电商下单案例)

以电商下单场景为例,涉及 3 个微服务:订单服务(TM,发起全局事务)、库存服务(RM)、账户服务(RM),实现"下单→扣库存→扣余额"的分布式事务控制,实操步骤如下:

2.2.1 业务准备
  • 订单服务:创建订单表(order),提供下单接口,调用库存服务扣减库存、账户服务扣减余额;

  • 库存服务:创建库存表(inventory),提供扣减库存接口;

  • 账户服务:创建账户表(account),提供扣减余额接口;

  • 三个服务均完成 Seata 前置配置(依赖、application.yml、数据源代理),事务组名称一致。

2.2.2 库存服务(RM)代码实现
java 复制代码
// 库存 mapper
@Mapper
public interface InventoryMapper {
    // 扣减库存
    @Update("update inventory set stock = stock - #{num} where product_id = #{productId} and stock >= #{num}")
    int deductStock(@Param("productId") Long productId, @Param("num") Integer num);
}

// 库存 service
@Service
public class InventoryService {

    @Autowired
    private InventoryMapper inventoryMapper;

    // 扣减库存接口(RM 分支事务,无需额外注解,Seata 自动拦截)
    public boolean deductStock(Long productId, Integer num) {
        // 执行本地事务:扣减库存
        int rows = inventoryMapper.deductStock(productId, num);
        // 若影响行数为0,说明库存不足,抛出异常,触发全局回滚
        if (rows == 0) {
            throw new RuntimeException("库存不足");
        }
        return true;
    }
}
2.2.3 账户服务(RM)代码实现
java 复制代码
// 账户 mapper
@Mapper
public interface AccountMapper {
    // 扣减余额
    @Update("update account set balance = balance - #{amount} where user_id = #{userId} and balance >= #{amount}")
    int deductBalance(@Param("userId") Long userId, @Param("amount") BigDecimal amount);
}

// 账户 service
@Service
public class AccountService {

    @Autowired
    private AccountMapper accountMapper;

    // 扣减余额接口(RM 分支事务,无需额外注解)
    public boolean deductBalance(Long userId, BigDecimal amount) {
        int rows = accountMapper.deductBalance(userId, amount);
        if (rows == 0) {
            throw new RuntimeException("余额不足");
        }
        return true;
    }
}
2.2.4 订单服务(TM)代码实现(核心,开启全局事务)
java 复制代码
// 订单 mapper
@Mapper
public interface OrderMapper {
    // 创建订单
    @Insert("insert into `order`(user_id, product_id, amount, status) values(#{userId}, #{productId}, #{amount}, 0)")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void createOrder(Order order);
}

// 订单 service(TM,发起全局事务)
@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private InventoryFeignClient inventoryFeignClient; // OpenFeign 调用库存服务
    @Autowired
    private AccountFeignClient accountFeignClient; // OpenFeign 调用账户服务

    // 开启全局事务:@GlobalTransactional 注解是核心,标记该方法为全局事务入口
    @GlobalTransactional(rollbackFor = Exception.class)
    public void createOrder(Long userId, Long productId, Integer num, BigDecimal amount) {
        try {
            // 1. 本地事务:创建订单(订单服务 RM 分支事务)
            Order order = new Order();
            order.setUserId(userId);
            order.setProductId(productId);
            order.setAmount(amount);
            orderMapper.createOrder(order);

            // 2. 远程调用:扣减库存(库存服务 RM 分支事务)
            inventoryFeignClient.deductStock(productId, num);

            // 3. 远程调用:扣减余额(账户服务 RM 分支事务)
            accountFeignClient.deductBalance(userId, amount);

            // 4. 本地事务:更新订单状态为"已完成"
            // (若前面步骤无异常,全局事务提交后,该操作生效)
        } catch (Exception e) {
            // 若任意一步抛出异常,触发全局回滚,所有已执行的操作(创建订单、扣库存、扣余额)都会回滚
            throw new RuntimeException("下单失败,触发全局回滚", e);
        }
    }
}

// 订单 controller
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @PostMapping("/create")
    public Result createOrder(@RequestParam Long userId,
                              @RequestParam Long productId,
                              @RequestParam Integer num,
                              @RequestParam BigDecimal amount) {
        try {
            orderService.createOrder(userId, productId, num, amount);
            return Result.success("下单成功");
        } catch (Exception e) {
            return Result.fail(e.getMessage());
        }
    }
}
2.2.5 测试验证
  1. 正常场景:库存充足、余额充足,三个服务的本地事务均执行成功,全局事务提交,订单创建成功、库存和余额正常扣减;

  2. 异常场景:库存充足,但余额不足,账户服务抛出异常,触发全局回滚,订单创建、库存扣减操作全部回滚,数据恢复到初始状态。

2.3 AT 模式优缺点

优点
  • 无代码侵入:只需添加 @GlobalTransactional 注解,无需编写补偿逻辑,开发成本极低;

  • 性能优异:一阶段本地事务提交,释放数据库锁,避免长期锁表,性能接近本地事务;

  • 自动回滚:基于 undo log 自动完成回滚,无需手动干预;

  • 适配广泛:支持所有关系型数据库(MySQL、Oracle 等),完美适配 Spring Cloud 常规业务。

缺点
  • 仅支持关系型数据库:不支持非关系型数据库(如 Redis)、消息队列等非 DB 资源;

  • 隔离级别较低:默认隔离级别为「读未提交」,若有高隔离级别需求(如读已提交、可重复读),需额外处理;

  • 依赖 SQL 解析:Seata 需拦截 SQL 生成 undo log,若 SQL 过于复杂(如复杂子查询),可能解析失败。

2.4 适用场景

Spring Cloud 微服务中的常规业务场景,尤其是:① 所有操作都基于关系型数据库;② 对性能要求高;③ 不想编写补偿逻辑;④ 对隔离级别要求不高(读未提交可接受)。例如:电商下单、支付回调、订单状态更新等场景,是 Seata 最主流的使用场景。

三、Seata TCC 模式(金融级强一致,自定义补偿)

TCC 模式是 Seata 提供的「强一致性」分布式事务模式,核心是"手动定义三个操作:Try(资源预留)、Confirm(确认提交)、Cancel(取消回滚)",不依赖数据库事务,可适配非关系型数据库、消息队列等非 DB 资源,是金融级业务(如转账、支付)的首选方案,能够实现强一致性保障。

与 AT 模式的"自动补偿"不同,TCC 模式的补偿逻辑需要开发者手动编写,侵入性较高,但灵活性极强,能够满足高一致性、高定制化的业务需求。

3.1 TCC 模式核心原理(三阶段操作,手动补偿)

TCC 模式基于「Try-Confirm-Cancel」三阶段操作,核心是"先预留资源,再确认提交,失败则取消预留",全程由开发者控制,原理拆解如下:

1. Try 阶段(资源检查与预留)

所有参与分布式事务的服务,执行资源检查和预留操作,确保后续操作能够成功执行,预留的资源是"不可用"状态,避免并发问题。例如:转账场景中,Try 阶段检查余额是否充足,并将需要转账的金额冻结(预留资源),此时余额不可用。

2. Confirm 阶段(确认提交)

若所有服务的 Try 阶段都执行成功,TM 发起全局提交,各服务执行 Confirm 操作,将 Try 阶段预留的资源"确认生效",且 Confirm 操作必须是「幂等」的(多次执行结果一致)。例如:转账场景中,Confirm 阶段将冻结的金额转入目标账户,释放预留资源。

3. Cancel 阶段(取消回滚)

若有任意一个服务的 Try 阶段失败,TM 发起全局回滚,各服务执行 Cancel 操作,释放 Try 阶段预留的资源,恢复到初始状态,Cancel 操作也必须是「幂等」的。例如:转账场景中,Cancel 阶段解冻冻结的金额,恢复到转账前的余额。

关键说明:TCC 模式不依赖数据库事务,Try、Confirm、Cancel 三个操作均由开发者手动实现,因此可以适配非关系型数据库(如 Redis)、消息队列等非 DB 资源;同时,由于 Try 阶段已预留资源,Confirm/Cancel 阶段执行速度快,能够实现强一致性。

3.2 Spring Cloud + Seata TCC 模式实操(转账案例)

以金融转账场景为例,涉及 2 个微服务:转账服务(TM,发起全局事务)、账户服务(RM,包含两个账户,执行转账操作),实现"账户A转账给账户B"的分布式事务控制,实操步骤如下:

3.2.1 业务准备
  • 账户服务:创建账户表(account),增加「冻结金额」字段(freeze_balance),用于 Try 阶段的资源预留;

  • 转账服务:调用账户服务的 Try、Confirm、Cancel 接口,发起全局事务;

  • 两个服务均完成 Seata 前置配置(无需配置数据源代理,TCC 模式不依赖 DB 事务)。

3.2.2 账户服务(RM):定义 TCC 接口(Try/Confirm/Cancel)

Seata TCC 模式要求,每个 RM 需定义 TCC 接口,通过 @TwoPhaseBusinessAction 注解标记 Try 方法,并指定 Confirm 和 Cancel 方法名称,Seata 会自动拦截调用,协调三个阶段的执行。

java 复制代码
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;

/**
 * 账户服务 TCC 接口(核心:定义 Try/Confirm/Cancel 三阶段方法)
 */
public interface AccountTccService {

    /**
     * Try 阶段:资源检查与预留(冻结转账金额)
     * @param actionContext 事务上下文(用于传递全局事务ID、参数等)
     * @param userId 账户ID
     * @param amount 转账金额
     * @return 预留是否成功
     */
    @TwoPhaseBusinessAction(name = "transferTcc", commitMethod = "confirm", rollbackMethod = "cancel")
    boolean tryFreeze(BusinessActionContext actionContext,
                      @BusinessActionContextParameter(paramName = "userId") Long userId,
                      @BusinessActionContextParameter(paramName = "amount") BigDecimal amount);

    /**
     * Confirm 阶段:确认提交(扣除冻结金额,完成转账)
     * @param actionContext 事务上下文
     * @return 提交是否成功
     */
    boolean confirm(BusinessActionContext actionContext);

    /**
     * Cancel 阶段:取消回滚(解冻冻结金额,恢复初始状态)
     * @param actionContext 事务上下文
     * @return 回滚是否成功
     */
    boolean cancel(BusinessActionContext actionContext);
}

// TCC 接口实现类
@Service
public class AccountTccServiceImpl implements AccountTccService {

    @Autowired
    private AccountMapper accountMapper;

    /**
     * Try 阶段:检查余额是否充足,冻结转账金额
     */
    @Override
    public boolean tryFreeze(BusinessActionContext actionContext, Long userId, BigDecimal amount) {
        // 1. 检查余额是否充足(可用余额 = 余额 - 冻结金额)
        Account account = accountMapper.selectById(userId);
        if (account == null || account.getBalance().subtract(account.getFreezeBalance()).compareTo(amount) < 0) {
            return false; // 余额不足,预留失败
        }
        // 2. 冻结金额(预留资源):freeze_balance = freeze_balance + amount
        int rows = accountMapper.freezeBalance(userId, amount);
        return rows > 0;
    }

    /**
     * Confirm 阶段:扣除冻结金额,完成转账(幂等实现:避免重复扣除)
     */
    @Override
    public boolean confirm(BusinessActionContext actionContext) {
        // 从上下文获取参数(Try 阶段传递的参数)
        Long userId = Long.parseLong(actionContext.getActionContext("userId").toString());
        BigDecimal amount = new BigDecimal(actionContext.getActionContext("amount").toString());
        // 扣除冻结金额:balance = balance - amount,freeze_balance = freeze_balance - amount
        // 幂等处理:添加版本号或判断冻结金额是否大于等于转账金额
        int rows = accountMapper.confirmFreeze(userId, amount);
        return rows > 0;
    }

    /**
     * Cancel 阶段:解冻冻结金额(幂等实现:避免重复解冻)
     */
    @Override
    public boolean cancel(BusinessActionContext actionContext) {
        Long userId = Long.parseLong(actionContext.getActionContext("userId").toString());
        BigDecimal amount = new BigDecimal(actionContext.getActionContext("amount").toString());
        // 解冻金额:freeze_balance = freeze_balance - amount
        int rows = accountMapper.cancelFreeze(userId, amount);
        return rows > 0;
    }
}
3.2.3 转账服务(TM):发起全局事务,调用 TCC 接口
java 复制代码
@Service
public class TransferService {

    @Autowired
    private AccountFeignClient accountFeignClient; // OpenFeign 调用账户服务 TCC 接口

    // 开启全局事务,调用两个账户的 Try 方法,Seata 自动协调 Confirm/Cancel
    @GlobalTransactional(rollbackFor = Exception.class)
    public boolean transfer(Long fromUserId, Long toUserId, BigDecimal amount) {
        // 1. 调用账户服务:冻结转出账户的金额(Try 阶段)
        boolean fromSuccess = accountFeignClient.tryFreeze(fromUserId, amount);
        // 2. 调用账户服务:冻结转入账户的金额(Try 阶段,转入账户需预留"接收金额"的资源,实际可简化)
        boolean toSuccess = accountFeignClient.tryFreeze(toUserId, amount.negate()); // 负数表示转入预留

        // 若任意一个 Try 阶段失败,Seata 自动调用两个账户的 Cancel 方法,触发回滚
        if (!fromSuccess || !toSuccess) {
            throw new RuntimeException("转账预留资源失败,触发回滚");
        }

        // 所有 Try 阶段成功,Seata 自动调用两个账户的 Confirm 方法,完成全局提交
        return true;
    }
}

3.3 TCC 模式优缺点

优点
  • 强一致性:Try 阶段预留资源,Confirm/Cancel 阶段快速执行,能够实现分布式事务的强一致性,满足 ACID 原则;

  • 灵活性高:不依赖数据库事务,可适配非关系型数据库(Redis)、消息队列、第三方 API 等非 DB 资源;

  • 性能较好:Try 阶段预留资源,Confirm/Cancel 阶段无锁操作,执行速度快;

  • 隔离级别可自定义:开发者可根据业务需求,自定义隔离级别,避免脏读、不可重复读等问题。

缺点
  • 侵入性高:需手动编写 Try、Confirm、Cancel 三个方法,开发成本高,对开发者要求高;

  • 需处理幂等性:Confirm 和 Cancel 方法可能被多次调用(如网络超时重试),必须实现幂等性,否则会出现数据异常;

  • 开发复杂度高:需考虑资源预留、补偿逻辑、幂等性等细节,容易出现 bugs。

3.4 适用场景

Spring Cloud 微服务中对一致性要求高、需适配非 DB 资源的场景,尤其是:① 金融级业务(转账、支付、对账);② 涉及非关系型数据库(Redis)、第三方 API(如支付接口);③ 对隔离级别有自定义需求。例如:银行转账、电商支付、跨境结算等场景。

四、Seata SAGA 模式(长事务,复杂流程,最终一致)

SAGA 模式是 Seata 提供的「长事务解决方案」,核心是"将长事务拆分为多个短事务,每个短事务执行本地事务提交,失败时通过补偿事务回滚",适用于事务流程长、涉及多个服务、无法实现强一致性的场景,最终实现数据的最终一致性,是复杂业务流程的首选方案。

与 AT、TCC 模式不同,SAGA 模式不依赖锁机制,每个短事务执行后立即提交,释放资源,适合执行时间长、涉及多个服务的复杂场景(如旅行预订、供应链管理)。

4.1 SAGA 模式核心原理(长事务拆分+补偿回滚)

SAGA 模式的核心思想是「拆分长事务」,将一个复杂的长事务,拆分为多个相互独立的短事务(每个短事务对应一个服务的操作),每个短事务执行本地事务并提交,同时为每个短事务编写对应的「补偿事务」(用于回滚),原理拆解如下:

  1. 拆分长事务:将复杂的业务流程,拆分为 N 个短事务(T1、T2、T3、...、Tn),每个短事务由一个微服务执行,执行完成后立即提交本地事务,释放资源;

  2. 执行正向流程:按顺序执行 T1、T2、T3、...、Tn,若所有短事务都执行成功,全局事务完成;

  3. 执行补偿流程:若某个短事务 Tk 执行失败,触发补偿流程,按逆序执行补偿事务(Ck-1、Ck-2、...、C1),每个补偿事务用于回滚对应短事务的操作,最终实现数据的最终一致性。

关键说明:SAGA 模式的每个短事务都直接提交,无需等待其他事务,因此不存在锁表问题,适合长事务场景;补偿事务由开发者手动编写,且需保证幂等性;SAGA 模式不保证隔离性,可能出现脏读、脏写,需在业务层面处理。

Seata 提供两种 SAGA 模式实现:① 简单 SAGA 模式(手动编写补偿逻辑,适用于简单长事务);② 状态机 SAGA 模式(基于状态机定义业务流程和补偿逻辑,适用于复杂长事务,支持流程编排、分支判断等),其中状态机模式是企业实战的首选。

4.2 Spring Cloud + Seata SAGA 模式实操(旅行预订案例)

以旅行预订场景为例,涉及 3 个微服务:预订服务(TM,发起长事务)、机票服务(RM)、酒店服务(RM)、景点门票服务(RM),业务流程为"预订机票→预订酒店→预订门票",若任意一步失败,需回滚前面所有已完成的预订,实操步骤如下(采用简单 SAGA 模式):

4.2.1 业务准备
  • 机票服务:提供"预订机票"接口(正向事务)和"取消机票"接口(补偿事务);

  • 酒店服务:提供"预订酒店"接口(正向事务)和"取消酒店"接口(补偿事务);

  • 门票服务:提供"预订门票"接口(正向事务)和"取消门票"接口(补偿事务);

  • 预订服务:按顺序调用三个服务的正向接口,失败时调用对应补偿接口,发起全局事务。

4.2.2 各 RM 服务:实现正向事务与补偿事务
java 复制代码
// 机票服务 service(正向事务+补偿事务)
@Service
public class FlightService {

    @Autowired
    private FlightMapper flightMapper;

    // 正向事务:预订机票
    public Long bookFlight(Long userId, String flightNo, Date date) {
        // 执行本地事务:创建机票预订记录
        FlightOrder flightOrder = new FlightOrder();
        flightOrder.setUserId(userId);
        flightOrder.setFlightNo(flightNo);
        flightOrder.setDate(date);
        flightOrder.setStatus(1); // 1-已预订
        flightMapper.insert(flightOrder);
        return flightOrder.getId(); // 返回预订ID,用于补偿
    }

    // 补偿事务:取消机票(幂等实现)
    public boolean cancelFlight(Long flightOrderId) {
        // 幂等处理:判断订单状态,避免重复取消
        FlightOrder flightOrder = flightMapper.selectById(flightOrderId);
        if (flightOrder == null || flightOrder.getStatus() == 0) {
            return true; // 已取消或不存在,视为成功
        }
        // 执行补偿:更新订单状态为"已取消"
        flightOrder.setStatus(0);
        flightMapper.updateById(flightOrder);
        return true;
    }
}

// 酒店服务、门票服务类似,均提供正向预订接口和补偿取消接口,此处省略...
4.2.3 预订服务(TM):发起 SAGA 长事务,协调正向与补偿流程
java 复制代码
@Service
public class BookingService {

    @Autowired
    private FlightFeignClient flightFeignClient;
    @Autowired
    private HotelFeignClient hotelFeignClient;
    @Autowired
    private TicketFeignClient ticketFeignClient;

    // 开启全局事务,Seata 协调 SAGA 流程(失败时自动触发补偿)
    @GlobalTransactional(rollbackFor = Exception.class)
    public boolean bookTravel(Long userId, String flightNo, Date flightDate, String hotelName, String ticketNo) {
        // 存储各服务的预订ID,用于补偿
        Long flightOrderId = null;
        Long hotelOrderId = null;
        Long ticketOrderId = null;

        try {
            // 1. 正向流程:预订机票(短事务1)
            flightOrderId = flightFeignClient.bookFlight(userId, flightNo, flightDate);

            // 2. 正向流程:预订酒店(短事务2)
            hotelOrderId = hotelFeignClient.bookHotel(userId, hotelName, flightDate);

            // 3. 正向流程:预订门票(短事务3)
            ticketOrderId = ticketFeignClient.bookTicket(userId, ticketNo, flightDate);

            // 所有短事务执行成功,全局事务完成
            return true;

        } catch (Exception e) {
            // 执行补偿流程:按逆序取消已预订的服务
            if (ticketOrderId != null) {
                ticketFeignClient.cancelTicket(ticketOrderId); // 补偿3
            }
            if (hotelOrderId != null) {
                hotelFeignClient.cancelHotel(hotelOrderId); // 补偿2
            }
            if (flightOrderId != null) {
                flightFeignClient.cancelFlight(flightOrderId); // 补偿1
            }
            throw new RuntimeException("旅行预订失败,已触发补偿回滚", e);
        }
    }
}
4.2.4 状态机 SAGA 模式说明(企业实战推荐)

对于复杂的长事务(如包含分支判断、并发执行、子流程),推荐使用 Seata 状态机 SAGA 模式,通过可视化状态机设计器定义业务流程和补偿逻辑,无需手动编写补偿流程的调用顺序,Seata 状态机引擎会自动驱动流程执行和补偿回滚,降低开发复杂度。

核心步骤:① 通过 Seata 状态机设计器,绘制业务流程(正向流程+补偿流程),生成 JSON 配置文件;② 在 Spring Cloud 微服务中配置状态机,指定流程配置文件;③ 调用状态机接口,发起长事务,Seata 自动协调流程执行和补偿。

4.3 SAGA 模式优缺点

优点
  • 适合长事务:每个短事务立即提交,无锁表问题,可处理执行时间长、涉及多个服务的复杂业务流程;

  • 性能优异:无需等待其他事务,无锁操作,执行速度快,支持高并发;

  • 灵活性高:可适配复杂业务流程(分支、并发、子流程),支持非 DB 资源;

  • 补偿逻辑简单:补偿事务只需回滚对应短事务的操作,无需考虑全局状态。

缺点
  • 仅保证最终一致性:不保证强一致性,在补偿流程执行前,可能出现数据不一致(如预订机票成功,但酒店预订失败,补偿前机票已预订);

  • 无隔离性:多个 SAGA 事务同时操作同一数据,可能出现脏读、脏写,需在业务层面处理(如添加版本号、乐观锁);

  • 需处理幂等性和空补偿:补偿事务可能被多次调用,需实现幂等性;同时需允许空补偿(原服务未执行,补偿服务执行),避免悬挂问题。

4.4 适用场景

Spring Cloud 微服务中长事务、复杂业务流程场景,尤其是:① 事务执行时间长(如旅行预订、供应链管理、审批流程);② 涉及多个服务,无法实现强一致性;③ 对性能要求高,不允许锁表。例如:旅行套餐预订、电商供应链履约、企业审批流程等场景。

五、Seata XA 模式(传统两阶段,强一致,兼容传统数据库)

XA 模式是 Seata 提供的「基于 XA 协议的传统两阶段提交模式」,核心是"依赖数据库原生的 XA 协议,实现分布式事务的强一致性",几乎无代码侵入,兼容所有支持 XA 协议的关系型数据库(MySQL、Oracle 等),适合传统数据库迁移到微服务的场景,或对强一致性要求高、可接受性能损耗的场景。

XA 协议是 X/Open 组织定义的分布式事务标准,大多数主流关系型数据库都原生支持,Seata XA 模式只是对 XA 协议的封装,适配 Spring Cloud 微服务架构,简化开发流程。

5.1 XA 模式核心原理(数据库原生两阶段提交)

XA 模式依赖数据库原生的 XA 协议,采用「两阶段提交」机制,核心是"一阶段准备,二阶段提交/回滚",全程由数据库和 Seata TC 协同完成,原理拆解如下:

第一阶段(准备阶段)
  1. TM 开启全局事务,向 TC 注册全局事务,获取 XID;

  2. 各 RM 执行本地事务,但不提交,而是向数据库发送 XA prepare 指令,数据库将事务状态标记为"准备就绪",并锁定相关资源;

  3. RM 向 TC 汇报分支事务状态(准备就绪/失败)。

第二阶段(提交/回滚阶段)
  • 全局提交:若所有 RM 都准备就绪,TC 向所有 RM 发送 XA commit 指令,数据库执行提交操作,释放资源,全局事务完成;

  • 全局回滚:若有任意一个 RM 准备失败,TC 向所有 RM 发送 XA rollback 指令,数据库执行回滚操作,释放资源,全局事务回滚完成。

关键说明:XA 模式的核心依赖数据库原生的 XA 协议,Seata 仅负责协调 TC 和 RM 的通信,无需编写补偿逻辑,几乎无代码侵入;但由于一阶段需要锁定资源,直到二阶段完成才释放,因此性能较差,适合对性能要求不高、对强一致性要求高的场景。

5.2 Spring Cloud + Seata XA 模式实操(简单订单场景)

XA 模式的实操与 AT 模式非常相似,核心区别是「数据源代理模式不同」,无需编写任何额外代码,只需修改配置,即可切换为 XA 模式,步骤如下:

5.2.1 修改客户端配置(所有参与事务的微服务)

将 Seata 数据源代理模式改为 XA,其余配置与 AT 模式一致:

bash 复制代码
seata:
  data-source-proxy-mode: XA # 改为 XA 模式(AT 模式为 AT)
  registry:
    type: nacos
    nacos:
      server-addr: localhost:8848
      group: SEATA_GROUP
      namespace: public
  config:
    type: nacos
    nacos:
      server-addr: localhost:8848
      group: SEATA_GROUP
      namespace: public
  tx-service-group: my_seata_group # 需与 Seata Server 事务组一致

spring:
  cloud:
    alibaba:
      seata:
        tx-service-group: my_seata_group
        service:
          vgroup-mapping:
            my_seata_group: default
          grouplist:
            default: localhost:8091
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/order_db?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456

关键说明:XA 模式同样需要数据源代理,因此无需删除 AT 模式的数据源代理配置类(SeataDataSourceConfig),仅修改 data-source-proxy-mode 即可,Seata 会自动适配 XA 模式的数据源代理逻辑。

5.2.2 业务代码实现(与 AT 模式完全一致)

XA 模式的业务代码与 AT 模式完全相同,无需修改任何业务逻辑,仅需保证 TM 服务添加 @GlobalTransactional注解,RM 服务正常提供业务接口即可。以下以订单服务(TM)、库存服务(RM)为例,简化展示核心代码(与 AT 模式复用,无需额外开发):

库存服务(RM)核心代码
java 复制代码
// 库存 mapper(与 AT 模式一致)
@Mapper
public interface InventoryMapper {
    @Update("update inventory set stock = stock - #{num} where product_id = #{productId} and stock >= #{num}")
    int deductStock(@Param("productId") Long productId, @Param("num") Integer num);
}

// 库存 service(无需额外注解,Seata 自动拦截 XA 事务)
@Service
public class InventoryService {
    @Autowired
    private InventoryMapper inventoryMapper;

    public boolean deductStock(Long productId, Integer num) {
        int rows = inventoryMapper.deductStock(productId, num);
        if (rows == 0) {
            throw new RuntimeException("库存不足");
        }
        return true;
    }
}
订单服务(TM)核心代码
java 复制代码
@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private InventoryFeignClient inventoryFeignClient;
    @Autowired
    private AccountFeignClient accountFeignClient;

    // 全局事务注解,与 AT 模式一致,Seata 自动适配 XA 模式
    @GlobalTransactional(rollbackFor = Exception.class)
    public void createOrder(Long userId, Long productId, Integer num, BigDecimal amount) {
        try {
            // 1. 本地事务:创建订单
            Order order = new Order();
            order.setUserId(userId);
            order.setProductId(productId);
            order.setAmount(amount);
            orderMapper.createOrder(order);

            // 2. 远程调用:扣减库存
            inventoryFeignClient.deductStock(productId, num);

            // 3. 远程调用:扣减余额
            accountFeignClient.deductBalance(userId, amount);
        } catch (Exception e) {
            throw new RuntimeException("下单失败,触发全局回滚", e);
        }
    }
}
5.2.3 测试验证(与 AT 模式逻辑一致)
  1. 正常场景:库存充足、余额充足,所有 RM 执行本地事务并完成 XA 准备,TC 发起全局提交,所有数据库执行提交操作,数据一致;

  2. 异常场景:任意一个 RM 执行失败(如余额不足),该 RM 向 TC 汇报准备失败,TC 向所有 RM 发送回滚指令,所有数据库执行回滚,数据恢复初始状态;

  3. 特殊验证:测试过程中可中断 Seata Server 或网络,观察数据库状态------此时 RM 处于"准备就绪"状态,资源被锁定,直至 Seata Server 恢复后,TC 重新协调提交或回滚,验证 XA 模式的强一致性。

5.3 XA 模式优缺点

优点
  • 几乎无代码侵入:与 AT 模式一致,只需修改配置,无需编写补偿逻辑,开发成本低,适配 Spring Cloud 快速集成需求;

  • 强一致性:依赖数据库原生 XA 协议,严格遵循两阶段提交,保证分布式事务的强一致性,满足 ACID 原则;

  • 兼容性强:兼容所有支持 XA 协议的关系型数据库(MySQL、Oracle、SQL Server 等),适合传统数据库迁移到微服务的场景;

  • 实现简单:Seata 封装了 XA 协议的协调逻辑,无需开发者手动处理两阶段提交的细节,运维成本低。

缺点
  • 性能较差:一阶段需锁定数据库资源,直至二阶段提交/回滚后才释放,容易出现锁表、阻塞问题,并发量高的场景下性能损耗明显;

  • 仅支持关系型数据库:与 AT 模式一样,不支持非关系型数据库(Redis)、消息队列等非 DB 资源;

  • 数据库依赖:依赖数据库对 XA 协议的支持,部分轻量级数据库(如 SQLite)不支持 XA 协议,无法使用;

  • 无降级方案:若数据库出现异常,XA 事务无法降级,可能导致整个分布式事务阻塞,影响业务可用性。

5.4 适用场景

Spring Cloud 微服务中对强一致性要求高、性能要求不高,且基于传统关系型数据库的场景,尤其是:① 传统单体应用迁移到微服务,原有业务基于 XA 事务;② 对数据一致性要求极高(如金融对账、账务统计),可接受性能损耗;③ 业务场景简单,并发量低,无锁表风险。例如:传统金融系统的账务处理、企业内部的财务统计等场景。

六、Seata 四大模式对比总结与生产环境选型建议

Seata 的 AT、TCC、SAGA、XA 四种模式,分别适配不同的业务场景,无绝对优劣之分,核心是"贴合业务需求选择"。以下通过对比表格清晰呈现四大模式的核心差异,同时给出生产环境的选型指南,帮助开发者快速决策。

6.1 四大模式核心对比

对比维度 AT 模式 TCC 模式 SAGA 模式 XA 模式
一致性 最终一致(默认),可优化为强一致 强一致 最终一致 强一致
代码侵入性 极低(仅需 @GlobalTransactional) 高(手动编写 Try/Confirm/Cancel) 中(手动编写补偿事务) 极低(仅修改配置)
性能 优异(接近本地事务) 较好(无锁操作) 优异(无锁,短事务提交) 较差(锁表阻塞)
支持资源 仅关系型数据库 所有资源(DB、Redis、第三方 API) 所有资源(DB、Redis、第三方 API) 仅关系型数据库
隔离级别 默认读未提交(可优化) 自定义(开发者控制) 无(需业务层面处理) 依赖数据库(通常为可重复读)
幂等性要求 低(Seata 自动处理) 高(Confirm/Cancel 需手动实现) 高(补偿事务需手动实现) 低(数据库自动处理)
适用场景 常规业务、电商下单、支付回调 金融级业务、转账、支付对账 长事务、复杂流程、旅行预订 传统数据库迁移、高一致性低并发

6.2 生产环境选型指南

选型核心原则:优先匹配业务场景,平衡一致性、性能、开发成本,具体建议如下:

  1. 首选 AT 模式:若业务是 Spring Cloud 常规微服务场景,所有操作基于关系型数据库,对性能要求高、不想编写补偿逻辑,优先选择 AT 模式(覆盖 80% 以上的生产场景);

  2. 选择 TCC 模式:若业务是金融级场景(转账、支付),对强一致性要求极高,涉及非关系型数据库或第三方 API,选择 TCC 模式,需重点关注幂等性和补偿逻辑的编写;

  3. 选择 SAGA 模式:若业务是长事务、复杂流程(如旅行预订、供应链履约),执行时间长、涉及多个服务,无法实现强一致性,选择 SAGA 模式,推荐使用状态机模式降低开发复杂度;

  4. 选择 XA 模式:若业务是传统单体应用迁移到微服务,原有系统基于 XA 事务,或对强一致性要求极高、可接受性能损耗,选择 XA 模式,避免大量修改原有代码。

6.3 生产环境注意事项

  • Seata Server 高可用:生产环境必须集群部署 Seata Server,避免单点故障,同时配置 DB 模式存储事务状态,防止数据丢失;

  • 事务组配置一致:所有参与分布式事务的微服务,事务组名称(tx-service-group)必须一致,否则无法被 TC 协调;

  • 幂等性处理:TCC、SAGA 模式必须实现幂等性,避免网络超时重试导致的数据异常;

  • 性能优化:AT 模式可优化 SQL 解析效率,避免复杂子查询;XA 模式需控制并发量,减少锁表风险;

  • 监控与排查:启用 Seata 控制台,实时监控事务状态,针对失败事务,通过 undo log、事务日志排查问题。

七、总结

Seata 作为 Spring Cloud 生态中最主流的分布式事务解决方案,通过 AT、TCC、SAGA、XA 四种模式,覆盖了从常规业务到金融级业务、从短事务到长事务的所有场景。本文从核心原理、Spring Cloud 实操、优缺点、适用场景四个维度,完整拆解了四种模式的核心知识点,同时给出了生产环境选型建议,助力开发者快速上手 Seata,灵活解决微服务分布式事务难题。

相关推荐
better_liang3 天前
每日Java面试场景题知识点之-分布式事务
java·微服务·seata·分布式事务·一致性·saga·tcc
却话巴山夜雨时i4 天前
Java大厂面试:从Spring Boot到微服务的深度剖析
java·spring boot·spring cloud·微服务·分布式事务·大厂面试
都说名字长不会被发现5 天前
事务性发件箱模式设计与实现
数据库·分布式事务·幂等·事务性发件箱·可靠投递
leo_messi947 天前
2026版商城项目(二)-- 压力测试&缓存
java·缓存·压力测试·springcloud
總鑽風9 天前
springcloud2023_alibaba_sso单点登录_授权码模式(已跑通)
springcloud·单点登录·sso·授权码模式
總鑽風10 天前
springcloudalibaba2021-SSO 单点登录_密码模式
springcloud·alibaba·sso
奥升新能源平台13 天前
奥升充电最小化高可用机房部署方案
运维·安全·开源·能源·springcloud
恼书:-(空寄14 天前
Seata TCC 生产级(空回滚+悬挂+幂等)+ AT/TCC 混合使用
java·seata·分布式事务
leo_messi9416 天前
2026版商城项目(一)
java·elasticsearch·k8s·springcloud