从Docker启动Seata到Spring Boot集成,再通过一个订单-库存-账户的电商场景,把AT、TCC、SAGA、XA这四种模式的配置和用法都串一遍。


一、环境与版本说明

以下示例基于 Spring Boot 3.2.5 + Seata 1.8.0,这是官方明确支持Spring Boot 3的组合。如果你用的是Spring Boot 2.x,Seata 1.4~1.6也都可以。

组件规划:

  • Seata Server(TC):Docker部署,注册到Nacos
  • 三个微服务:order-service(订单)、stock-service(库存)、account-service(账户)
  • 注册中心:Nacos 2.3.2
  • 数据库:MySQL 8.0,每个服务有自己的库

二、Seata Server Docker部署

2.1 拉取并启动(临时获取配置)

docker pull seataio/seata-server:1.8.0

# 临时启动,只是为了拷贝配置文件出来
docker run -d -p 8091:8091 -p 7091:7091 --name seata-temp seataio/seata-server:1.8.0

# 拷贝配置到宿主机
mkdir -p /docker/seata/config
docker cp seata-temp:/seata-server/resources /docker/seata/config
docker rm -f seata-temp

2.2 修改配置 application.yml

关键配置是注册中心存储模式

server:
  port: 7091

seata:
  registry:
    type: nacos
    nacos:
      server-addr: 172.17.0.1:8848   # 容器内访问宿主机Nacos的IP
      namespace: public
      group: SEATA_GROUP
      cluster: default
  config:
    type: nacos
    nacos:
      server-addr: 172.17.0.1:8848
      namespace: public
      group: SEATA_GROUP
      data-id: seataServer.properties
  store:
    mode: db   # 生产环境用db,测试用file也行
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://172.17.0.1:3306/seata_server?useUnicode=true&characterEncoding=utf8
      user: root
      password: 123456

2.3 正式启动

docker run -d \
  --name seata-server \
  -p 8091:8091 \
  -p 7091:7091 \
  -v /docker/seata/config/resources:/seata-server/resources \
  seataio/seata-server:1.8.0

⚠️ 注意:如果用file模式存储,事务日志存本地文件,Server重启后事务状态会丢失。生产环境务必用db模式。


三、Spring Boot 项目集成(通用步骤)

3.1 引入依赖

每个微服务的 pom.xml 都要加:

<!-- Seata Spring Boot Starter -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.8.0</version>
</dependency>

<!-- Spring Cloud Alibaba Seata(如果用了Spring Cloud) -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <version>2023.0.1.0</version>
    <exclusions>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!-- Nacos 服务发现 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

这里排除了spring-cloud-starter-alibaba-seata自带的Seata版本,用我们指定的1.8.0,避免版本冲突。

3.2 配置文件 application.yml

spring:
  application:
    name: order-service
  datasource:
    url: jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: my_tx_group           # 事务组名
  service:
    vgroup-mapping:
      my_tx_group: default                # 映射到Seata Server的cluster
  registry:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace: public
      group: SEATA_GROUP
  config:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace: public
      group: SEATA_GROUP
  data-source-proxy-mode: AT              # 默认AT,也可改为XA

关键点tx-service-group要跟Seata Server端配置的vgroupMapping对应上。


四、数据库准备(AT/XA模式必需)

AT和XA模式依赖undo_log表,每个业务数据库都要创建

CREATE TABLE `undo_log` (
    `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
    `branch_id` BIGINT(20) NOT NULL,
    `xid` VARCHAR(100) NOT NULL,
    `context` VARCHAR(128) NOT NULL,
    `rollback_info` LONGBLOB NOT NULL,
    `log_status` INT(11) NOT NULL,
    `log_created` DATETIME NOT NULL,
    `log_modified` DATETIME NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Seata Server自己的库还需要global_tablebranch_tablelock_table三张表,从官方GitHub的script/server/db目录获取。


五、AT模式(最常用,无侵入)

AT模式是Seata的默认模式,对业务代码零侵入。

5.1 配置

application.yml中已设置 seata.data-source-proxy-mode: AT(默认就是AT,不配也行)。

5.2 代码示例

订单服务 - 发起方(TM)

@Service
public class OrderService {

    @Autowired
    private StockFeignClient stockFeignClient;
    @Autowired
    private AccountFeignClient accountFeignClient;
    @Autowired
    private OrderMapper orderMapper;

    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public void createOrder(OrderDTO order) {
        // 1. 本地:创建订单
        orderMapper.insert(order);
        
        // 2. 远程:扣减库存(调用stock-service)
        stockFeignClient.deductStock(order.getProductId(), order.getQuantity());
        
        // 3. 远程:扣减账户余额(调用account-service)
        accountFeignClient.decreaseBalance(order.getUserId(), order.getAmount());
    }
}

库存服务 - 参与方(RM)

@Service
public class StockService {
    
    @Autowired
    private StockMapper stockMapper;

    // 普通本地事务即可,Seata会自动代理DataSource
    @Transactional
    public void deductStock(Long productId, Integer quantity) {
        Stock stock = stockMapper.selectByProductId(productId);
        if (stock.getStock() < quantity) {
            throw new RuntimeException("库存不足");
        }
        stock.setStock(stock.getStock() - quantity);
        stockMapper.updateById(stock);
    }
}

AT模式原理:一阶段执行SQL时自动生成undo_log(记录修改前后镜像),二阶段如果成功则异步清理undo_log,如果失败则根据undo_log自动回滚。


六、TCC模式(高性能,需编码)

TCC需要手动实现 Try(预留)、Confirm(确认)、Cancel(取消) 三个方法。

6.1 配置

seata:
  data-source-proxy-mode: AT   # TCC不需要改这个,跟AT共用即可

6.2 定义TCC接口

@LocalTCC
public interface AccountTCCService {

    @TwoPhaseBusinessAction(name = "decreaseBalance", commitMethod = "confirm", rollbackMethod = "cancel")
    void tryDecreaseBalance(
        @BusinessActionContextParameter(paramName = "userId") Long userId,
        @BusinessActionContextParameter(paramName = "amount") BigDecimal amount
    );

    void confirm(BusinessActionContext context);

    void cancel(BusinessActionContext context);
}

6.3 实现TCC逻辑

@Service
public class AccountTCCServiceImpl implements AccountTCCService {

    @Autowired
    private AccountMapper accountMapper;

    @Override
    @Transactional
    public void tryDecreaseBalance(Long userId, BigDecimal amount) {
        // Try阶段:检查余额并冻结(预留资源)
        Account account = accountMapper.selectByUserId(userId);
        if (account.getBalance().compareTo(amount) < 0) {
            throw new RuntimeException("余额不足");
        }
        // 将可用余额减少,冻结金额增加
        account.setFrozen(account.getFrozen().add(amount));
        account.setBalance(account.getBalance().subtract(amount));
        accountMapper.updateById(account);
    }

    @Override
    @Transactional
    public void confirm(BusinessActionContext context) {
        // Confirm阶段:真正扣减,释放冻结
        Long userId = Long.valueOf(context.getActionContext("userId").toString());
        BigDecimal amount = new BigDecimal(context.getActionContext("amount").toString());
        Account account = accountMapper.selectByUserId(userId);
        account.setFrozen(account.getFrozen().subtract(amount));
        accountMapper.updateById(account);
    }

    @Override
    @Transactional
    public void cancel(BusinessActionContext context) {
        // Cancel阶段:回滚,释放冻结,恢复余额
        Long userId = Long.valueOf(context.getActionContext("userId").toString());
        BigDecimal amount = new BigDecimal(context.getActionContext("amount").toString());
        Account account = accountMapper.selectByUserId(userId);
        account.setFrozen(account.getFrozen().subtract(amount));
        account.setBalance(account.getBalance().add(amount));
        accountMapper.updateById(account);
    }
}

6.4 发起方调用

@Service
public class OrderService {

    @Autowired
    private AccountTCCService accountTCCService;

    @GlobalTransactional
    public void createOrder(OrderDTO order) {
        // ... 订单创建 ...
        
        // 调用TCC服务
        accountTCCService.tryDecreaseBalance(order.getUserId(), order.getAmount());
    }
}

TCC不依赖数据库事务,性能最好,但开发成本高,适合资金扣减等核心场景。


七、SAGA模式(长事务,流程编排)

SAGA适合流程长、步骤多的场景,通过状态机或注解方式编排正向操作和补偿操作。

7.1 引入依赖

<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-saga-starter</artifactId>
    <version>1.8.0</version>
</dependency>

7.2 定义状态机(JSON)

resources/statelang/order_saga.json

{
  "name": "orderSaga",
  "startState": "CreateOrder",
  "states": {
    "CreateOrder": {
      "type": "ServiceTask",
      "serviceName": "orderService",
      "serviceMethod": "createOrderLocal",
      "compensateMethod": "cancelOrderLocal",
      "next": "DeductStock",
      "catch": [{"exceptions": ["java.lang.Exception"], "next": "Failure"}]
    },
    "DeductStock": {
      "type": "ServiceTask",
      "serviceName": "stockService",
      "serviceMethod": "deductStockLocal",
      "compensateMethod": "restoreStockLocal",
      "next": "DecreaseBalance",
      "catch": [{"exceptions": ["java.lang.Exception"], "next": "Failure"}]
    },
    "DecreaseBalance": {
      "type": "ServiceTask",
      "serviceName": "accountService",
      "serviceMethod": "decreaseBalanceLocal",
      "compensateMethod": "increaseBalanceLocal",
      "next": "Success",
      "catch": [{"exceptions": ["java.lang.Exception"], "next": "Failure"}]
    },
    "Success": {"type": "Fail", "end": true},
    "Failure": {"type": "Fail", "end": true}
  }
}

7.3 发起SAGA事务

@Service
public class OrderSagaService {

    @Autowired
    private StateMachineEngine stateMachineEngine;

    @GlobalTransactional
    public void createOrder(OrderDTO order) {
        Map<String, Object> params = new HashMap<>();
        params.put("order", order);
        
        // 启动状态机
        stateMachineEngine.start("orderSaga", null, params);
    }
}

SAGA模式的官方示例可参考 seata-samples 项目。


八、XA模式(强一致,依赖数据库)

XA模式利用数据库自身的XA协议,强一致性但性能较低

8.1 配置

seata:
  data-source-proxy-mode: XA   # 改为XA模式

8.2 代码(跟AT几乎一样)

@Service
public class OrderService {

    @GlobalTransactional
    public void createOrder(OrderDTO order) {
        orderMapper.insert(order);
        stockFeignClient.deductStock(order.getProductId(), order.getQuantity());
        accountFeignClient.decreaseBalance(order.getUserId(), order.getAmount());
    }
}

XA模式代码跟AT一样无侵入,但性能最差(锁资源时间长),适合对一致性要求极高、并发不高的场景。


九、四种模式配置对比

模式data-source-proxy-mode额外依赖额外建表代码侵入
ATAT(默认)undo_log(每个业务库)
TCCATTCC高(需实现3个方法)
SAGAATseata-saga-starter中(需定义状态机)
XAXAundo_log(每个业务库)

十、完整Demo参考

如果想直接看完整可运行的项目,可以参考:

  1. 官方示例seata-samples — 包含各模式的Spring Boot示例
  2. 社区实战yimo-seata-samples — 同一项目中混合使用AT和TCC模式
  3. CSDN教程SpringBoot3.2.5整合Seata1.8.0详细教程 — 完整可运行代码

💡 实操建议

  • 先用AT模式跑通:代码改动最小,快速验证链路
  • 遇到锁冲突:调大 client.rm.lock.retry.timeout 参数
  • 跨服务XID传递:用Feign时,spring-cloud-starter-alibaba-seata会自动传递XID,不用额外处理
  • 生产环境:Seata Server至少部署3个节点,存储用DB模式

更多推荐