Skip to content

Seata 极简教程


一、基础理论

1. 定位

  • 分布式事务解决方案:解决微服务架构下跨多个数据库/服务的数据一致性问题。
  • 阿里开源,Spring Cloud Alibaba 官方推荐,对标阿里内部 DTX
  • 核心目标:高性能 + 对业务侵入低(尤其是 AT 模式)。

2. 核心角色

角色全称职责
TCTransaction Coordinator
事务协调者
独立进程,维护全局事务状态,驱动全局提交或回滚。
TMTransaction Manager
事务管理器
标注 @GlobalTransactional 的一方,负责开启、提交、回滚全局事务。
RMResource Manager
资源管理器
各分支服务,管理本地事务资源,向 TC 注册并执行分支事务提交/回滚。

3. 四种事务模式

模式原理侵入性适用场景
AT自动补偿:一阶段拦截 SQL 生成 undo_log;二阶段根据全局状态决定提交或回滚。(仅需加注解)80% 场景首选,CRUD 操作无缝兼容
TCC手动实现 Try / Confirm / Cancel 三阶段接口。复杂业务、需精细控制资源预留
Saga长事务拆分,每阶段正向操作 + 补偿操作;事务链顺序执行,失败时反向补偿。业务流程长、需异步化、不要求强隔离
XA基于 XA 协议的 2PC,数据库原生支持,一阶段不提交,持锁到二阶段。强一致性、兼容传统 2PC、可接受锁性能损耗

4. AT 模式核心原理(最常用)

AT 模式是 Seata 的默认模式,对业务零侵入,只需一个 @GlobalTransactional 注解。

一阶段(提交)

  1. TM 调用 @GlobalTransactional 方法,向 TC 申请生成 全局事务 XID
  2. XID 通过 RPC 透传到各分支服务。
  3. RM 拦截业务 SQL,解析生成 前后镜像(修改前数据 + 修改后数据)。
  4. RM 在本地事务中执行业务 SQL + 插入 undo_log(镜像数据),一并提交。
  5. RM 向 TC 注册分支事务,报告执行成功。

二阶段(全局提交/回滚)

  • 全局提交:TC 通知各 RM 异步删除 undo_log
  • 全局回滚:TC 通知各 RM 用 undo_log 中的前镜像生成反向 SQL,补偿回滚数据,再删除 undo_log

undo_log 拆解

  • 存储在业务库的 undo_log 表中(Seata 自动维护)。
  • 包含:前镜像 beforeImage、后镜像 afterImage、SQL 类型、表名、主键等。
  • 回滚时通过前镜像生成 UPDATE/DELETE/INSERT 反向 SQL,恢复数据到修改前状态。

5. 隔离级别与脏读问题

AT 模式默认读未提交(一阶段本地事务已提交),存在中间态脏读。

解决方案

  • ** @GlobalTransactional + @Transactional**:本地事务保证原子性,但全局视角仍可能读到未提交数据。
  • FOR UPDATE 代理:Seata 代理 SELECT FOR UPDATE,遇到未提交全局事务时申请全局锁等待,达到读已提交效果。

6. 与经典分布式事务方案对比

维度2PC/XATCCSagaSeata AT
一致性最终一致最终一致最终一致
性能低(锁资源久)较高(一阶段即提交)
侵入性高(需写3个接口)中(需写补偿)(仅需注解)
隔离性全局锁业务层控制默认读未提交,可用全局锁
回滚机制数据库驱动业务实现 Cancel业务实现补偿自动生成 undo_log 回滚
适用场景金融强一致库存/支付预留审批/物流长流程通用 CRUD 微服务

二、工作实践(复制即用)

1. 服务端部署(TC)

Docker 单机部署

bash
docker run --name seata-server \
  -p 8091:8091 \
  -p 7091:7091 \
  -e SEATA_PORT=8091 \
  -v /path/to/application.yml:/seata-server/resources/application.yml \
  -d seataio/seata-server:1.8.0
  • 8091:TC 业务端口(RM/TM 连接)。
  • 7091:TC 控制台端口(Web UI)。

TC 配置文件要点(application.yml)

yaml
server:
  port: 7091

seata:
  config:
    type: nacos        # 支持 nacos / file / etcd / consul
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: seata
      group: SEATA_GROUP
      data-id: seataServer.properties
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: seata
      group: SEATA_GROUP
  store:
    mode: db           # 事务日志存储方式:db / file / redis
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/seata
      user: root
      password: root

生产环境建议用 Nacos 作为配置中心,统一管理 Seata 的 registry/file 等配置。


2. 业务端依赖(RM / TM)

pom.xml

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

<!-- 若用 Nacos 作注册/配置中心 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

3. 业务端配置(application.yml)

yaml
seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: my_tx_group   # 事务分组,对应 TC 集群
  service:
    vgroup-mapping:
      my_tx_group: default        # 分组映射到 TC 集群名
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: seata
      group: SEATA_GROUP
  client:
    rm:
      async-commit-buffer-limit: 10000

4. 数据库准备(AT 模式)

每个业务库都需要创建 undo_log 表:

sql
CREATE TABLE IF NOT EXISTS `undo_log`
(
    `branch_id`     BIGINT       NOT NULL COMMENT '分支事务 ID',
    `xid`           VARCHAR(128) NOT NULL COMMENT '全局事务 XID',
    `context`       VARCHAR(128) NOT NULL COMMENT '上下文',
    `rollback_info` LONGBLOB     NOT NULL COMMENT '回滚信息(前/后镜像)',
    `log_status`    INT          NOT NULL COMMENT '0:正常 1:防御性保留',
    `log_created`   DATETIME(6)  NOT NULL COMMENT '创建时间',
    `log_modified`  DATETIME(6)  NOT NULL COMMENT '修改时间',
    UNIQUE INDEX `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4 COMMENT ='AT 模式 undo_log 表';

5. 全局事务使用(AT 模式)

业务入口(TM)

java
@Service
@Slf4j
public class OrderService {

    @Autowired
    private StorageClient storageClient;

    @Autowired
    private AccountClient accountClient;

    @Autowired
    private OrderMapper orderMapper;

    /**
     * 下单:创建订单 + 扣库存 + 扣余额
     * 只要任一环节异常,全局回滚
     */
    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public void createOrder(OrderCreateReq req) {
        // 1. 创建订单
        Order order = new Order();
        order.setUserId(req.getUserId());
        order.setCommodityCode(req.getCommodityCode());
        order.setCount(req.getCount());
        order.setMoney(req.getMoney());
        orderMapper.insert(order);

        // 2. 扣库存(RPC 调用 storage-service)
        storageClient.deduct(req.getCommodityCode(), req.getCount());

        // 3. 扣余额(RPC 调用 account-service)
        accountClient.debit(req.getUserId(), req.getMoney());

        // 任意步骤抛异常,Seata 驱动全局回滚,各服务自动通过 undo_log 恢复
    }
}

分支服务(RM)——以库存服务为例

java
@RestController
public class StorageController {

    @Autowired
    private StorageMapper storageMapper;

    @PostMapping("/storage/deduct")
    public void deduct(@RequestParam String commodityCode, @RequestParam Integer count) {
        // 普通业务方法即可,无需额外注解
        // Seata 代理数据源后自动拦截 SQL 生成 undo_log
        storageMapper.deduct(commodityCode, count);
    }
}

6. TCC 模式示例(高侵入但精细控制)

适用于需要预留资源的场景,如下单时冻结库存。

java
@LocalTCC
public interface StorageTccService {

    @TwoPhaseBusinessAction(name = "storageTccAction",
            commitMethod = "commit",
            rollbackMethod = "rollback")
    boolean tryDeduct(@BusinessActionContextParameter(paramName = "commodityCode") String commodityCode,
                      @BusinessActionContextParameter(paramName = "count") int count);

    boolean commit(BusinessActionContext context);

    boolean rollback(BusinessActionContext context);
}
java
@Service
@Slf4j
public class StorageTccServiceImpl implements StorageTccService {

    @Autowired
    private StorageMapper storageMapper;

    @Override
    public boolean tryDeduct(String commodityCode, int count) {
        // 检查库存并冻结(可用库存 - count,冻结库存 + count)
        return storageMapper.freezeStock(commodityCode, count) > 0;
    }

    @Override
    public boolean commit(BusinessActionContext context) {
        String commodityCode = context.getActionContext("commodityCode").toString();
        int count = Integer.parseInt(context.getActionContext("count").toString());
        // 真正扣减:冻结库存 - count,总库存 - count
        storageMapper.commitDeduct(commodityCode, count);
        return true;
    }

    @Override
    public boolean rollback(BusinessActionContext context) {
        String commodityCode = context.getActionContext("commodityCode").toString();
        int count = Integer.parseInt(context.getActionContext("count").toString());
        // 释放冻结库存
        storageMapper.unfreezeStock(commodityCode, count);
        return true;
    }
}

7. Saga 模式简介

适用于业务流程长、需异步化或遗留系统改造。

  • 状态机引擎(State Machine Engine):基于 JSON/DSL 定义状态流转图。
  • 编排式(Orchestration):由 Saga 编排器统一调用各服务正向操作 + 补偿操作。
  • 不保证隔离性,可能存在脏读,需业务层容忍或做幂等设计。

Saga 模式配置相对复杂,需引入 seata-saga-engine,此处不展开完整示例。


8. 数据源代理(AT 模式必须)

Seata AT 模式需要代理数据源才能拦截 SQL 生成 undo_log。

手动配置(若未自动生效)

java
@Configuration
public class SeataDataSourceConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Primary
    @Bean("dataSource")
    public DataSource dataSource(DataSource druidDataSource) {
        // Seata 代理数据源,开启 SQL 拦截
        return new DataSourceProxy(druidDataSource);
    }
}

Spring Cloud Alibaba Seata 通常自动代理,若发现 undo_log 未生成,检查是否手动多数据源未走代理。


三、核心速查表

概念说明
@GlobalTransactionalTM 开启全局事务;name 标识,rollbackFor 指定回滚异常
XID全局事务唯一标识,由 TC 生成,经 RPC 透传各服务
undo_logAT 模式回滚日志表,存前后镜像,业务库必须创建
TC事务协调者,独立部署,维护全局事务状态
TM事务管理器,发起方加 @GlobalTransactional
RM资源管理器,各分支服务,向 TC 注册并执行本地分支
tx-service-group事务分组,对应一套 TC 集群,业务端与 TC 配置需对齐
AT自动补偿模式,低侵入,80% 场景首选
TCCTry-Confirm-Cancel,高侵入,适合预留资源场景
Saga长事务 + 补偿,适合流程长、异步化场景
XA基于数据库 XA 协议的 2PC,强一致但性能低
DataSourceProxyAT 模式必须的数据源代理,拦截业务 SQL

四、排错速查

现象解决
@GlobalTransactional 不生效1. 检查是否引入了 seata-spring-boot-starter
2. 检查 tx-service-group 与 TC 的 vgroup-mapping 是否匹配
3. 检查 TC 是否正常运行,RM/TM 能否连接到 TC 的 8091 端口
undo_log 未生成 / 回滚失败1. 确认业务库已创建 undo_log
2. 确认数据源被 Seata 代理(DataSourceProxy
3. 检查数据库是否 InnoDB 引擎
全局事务提交但数据不一致1. 分支服务抛异常后被吞掉(如异步调用未等待结果)
2. RPC 框架未透传 XID(Feign 需确保请求头携带 TX_XID
TC 连接不上 / 注册失败1. 检查 seata.registry 配置(Nacos 地址、namespace、group)
2. 检查 TC 服务端 8091 端口是否暴露
3. 检查 Nacos 中 service.vgroup-mapping.my_tx_group 是否指向正确的 TC 集群名
TCC try 成功但 commit/rollback 不执行1. 检查 @TwoPhaseBusinessAction 的 commitMethod / rollbackMethod 方法名是否写对
2. 检查 commit/rollback 方法是否为 public 且参数为 BusinessActionContext
3. 检查是否被 Spring 管理(@Service / @Component
性能下降明显1. AT 模式下 SQL 拦截解析有开销,大表/批量操作建议拆分或改用 TCC
2. 检查是否 SELECT FOR UPDATE 频繁等待全局锁
3. 调整 client.rm.async-commit-buffer-limit 等参数
脏读问题AT 模式默认读未提交;如需读已提交,对关键查询加 FOR UPDATE,Seata 会自动申请全局锁
Spring Cloud Alibaba 版本冲突确认 spring-cloud-alibaba-dependenciesseata.version 兼容,参考官方版本说明