Skip to content

Seata 学习指南

导航目录

一、Seata 概述

1.1 什么是 Seata

Seata 是一套开源的分布式事务解决方案,主要用于解决微服务架构下多个服务、多个数据库之间的数据一致性问题。

  • 支持 AT 模式
  • 支持 TCC 模式
  • 支持 SAGA 模式
  • 支持 XA 模式

重点:Seata 解决的不是单库事务,而是“跨服务、跨库”的分布式事务问题。

1.2 Seata 能解决什么问题

例如下单场景:

  1. 订单服务创建订单
  2. 库存服务扣减库存
  3. 账户服务扣减余额

如果第二步或第三步失败,就需要回滚前面的操作,否则数据会不一致。

1.3 Seata 在微服务中的位置

text
客户端 -> Gateway -> order-service
                         |-> stock-service
                         |-> account-service
                         |-> Seata 协调全局事务

二、为什么需要分布式事务

2.1 本地事务和分布式事务的区别

2.1.1 本地事务

  • 一般只针对单个数据库连接
  • 例如 Spring 的 @Transactional

2.1.2 分布式事务

  • 涉及多个服务
  • 涉及多个数据库
  • 一个服务失败时,需要整体回滚或补偿

2.2 微服务为什么会引入事务一致性问题

  • 服务拆分后数据分散
  • 各服务独立数据库
  • 调用链变长
  • 网络不可靠

2.3 常见分布式事务方案

方案特点
2PC / XA强一致,但性能压力大
TCC侵入性较强,但控制精细
SAGA长事务补偿,适合长流程
AT开发成本低,适合常见业务

三、Seata 核心角色与工作原理

3.1 TC、TM、RM 是什么

3.1.1 TC

  • Transaction Coordinator
  • 事务协调者
  • 负责维护全局事务状态,驱动提交或回滚

3.1.2 TM

  • Transaction Manager
  • 事务管理器
  • 负责开启、提交、回滚全局事务

3.1.3 RM

  • Resource Manager
  • 资源管理器
  • 管理分支事务资源,执行本地提交或回滚

3.2 全局事务和分支事务

  • 全局事务:一个完整业务链路的事务
  • 分支事务:每个微服务内部对应的本地事务

3.3 Seata 工作流程

text
TM 开启全局事务
  -> TC 生成全局事务 XID
  -> XID 在服务间传播
  -> 各服务以 RM 身份注册分支事务
  -> 所有分支成功则提交
  -> 任一分支失败则全局回滚

3.4 XID 是什么

  • 全局事务唯一标识
  • 会在服务调用链路中持续传播

重点:Seata 能否生效,很大程度上取决于 XID 是否正确传播。

3.5 XID 传播示意

text
order-service 开启全局事务
 -> Seata 生成 XID
 -> OpenFeign / HTTP 调用自动透传 XID
 -> stock-service / account-service 获取 XID
 -> 注册分支事务

3.6 Seata 模式总览

模式特点侵入性适用场景
AT自动补偿,接入简单常规数据库更新
TCCTry / Confirm / Cancel资金、库存冻结
SAGA长事务补偿长链路流程
XA标准 2PC强一致数据库事务

四、Seata 部署与环境搭建

4.1 常见部署组成

  • Seata Server
  • 业务服务
  • 注册中心
  • 配置中心
  • 事务日志库

4.2 Seata Server 启动

bash
# 启动 Seata Server
seata-server.bat

Linux:

bash
sh seata-server.sh

4.3 Seata 与 Nacos 集成思路

通常会这样用:

  • Nacos 作为注册中心
  • Nacos 作为配置中心
  • Seata Server 注册到 Nacos

4.4 Nacos 注册中心配置示例

properties
seata.registry.type=nacos
seata.registry.nacos.server-addr=127.0.0.1:8848
seata.registry.nacos.group=SEATA_GROUP
seata.registry.nacos.namespace=dev-namespace-id
seata.registry.nacos.application=seata-server

4.5 配置中心示例

properties
seata.config.type=nacos
seata.config.nacos.server-addr=127.0.0.1:8848
seata.config.nacos.group=SEATA_GROUP
seata.config.nacos.namespace=dev-namespace-id

4.6 事务日志表说明

AT 模式通常会依赖这些表:

  • undo_log

undo_log 用于记录回滚快照。

sql
CREATE TABLE undo_log (
  id BIGINT NOT NULL AUTO_INCREMENT,
  branch_id BIGINT NOT NULL,
  xid VARCHAR(100) NOT NULL,
  context VARCHAR(128) NOT NULL,
  rollback_info LONGBLOB NOT NULL,
  log_status INT NOT NULL,
  log_created DATETIME NOT NULL,
  log_modified DATETIME NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY ux_undo_log (xid, branch_id)
);

4.7 Seata Server 常见配置项

properties
# Seata Server 监听端口
server.port=7091
# 存储模式,可选 file / db / redis
seata.store.mode=db
# 事务日志库驱动
store.db.driver-class-name=com.mysql.cj.jdbc.Driver
# 事务日志库地址
store.db.url=jdbc:mysql://127.0.0.1:3306/seata
# 事务日志库用户名
store.db.user=root
# 事务日志库密码
store.db.password=123456

4.8 数据库表准备说明

在实际项目中通常需要准备两类表:

  • 业务库中的 undo_log
  • Seata Server 自己使用的事务日志表

五、AT 模式

5.1 什么是 AT 模式

AT 是 Seata 默认、最常见的模式。

特点:

  • 开发成本低
  • 对业务代码侵入较小
  • 适合常见单库更新场景

5.2 AT 模式原理

AT 模式核心思路:

  1. 执行业务 SQL 前记录 before image
  2. 执行业务 SQL 后记录 after image
  3. 正常时提交本地事务
  4. 回滚时根据 undo_log 生成反向 SQL

5.3 AT 模式执行流程

text
开启全局事务
 -> 执行业务 SQL
 -> 记录 undo_log
 -> 分支事务注册
 -> 全局提交 / 全局回滚

5.4 AT 模式代码示例

java
@Service
public class OrderService {

    @Resource
    private StockFeignClient stockFeignClient;

    @Resource
    private AccountFeignClient accountFeignClient;

    @GlobalTransactional(name = "create-order-tx", rollbackFor = Exception.class)
    public void createOrder(Long userId, Long productId, Integer count, BigDecimal amount) {
        // 1. 创建订单
        saveOrder(userId, productId, count, amount);

        // 2. 扣减库存
        stockFeignClient.decrease(productId, count);

        // 3. 扣减余额
        accountFeignClient.decrease(userId, amount);

        // 如果这里抛异常,前面的分支事务会被全局回滚
        if (amount.compareTo(new BigDecimal("10000")) > 0) {
            throw new RuntimeException("订单金额过大,模拟全局事务回滚");
        }
    }

    private void saveOrder(Long userId, Long productId, Integer count, BigDecimal amount) {
        // 这里只演示事务主流程,实际项目中可调用 mapper 持久化订单
    }
}

5.5 Feign 接口示例

java
@FeignClient(name = "stock-service")
public interface StockFeignClient {

    @PostMapping("/stock/decrease")
    void decrease(@RequestParam("productId") Long productId,
                  @RequestParam("count") Integer count);
}
java
@FeignClient(name = "account-service")
public interface AccountFeignClient {

    @PostMapping("/account/decrease")
    void decrease(@RequestParam("userId") Long userId,
                  @RequestParam("amount") BigDecimal amount);
}

5.6 下游库存服务示例

java
@RestController
@RequestMapping("/stock")
public class StockController {

    @Resource
    private StockService stockService;

    @PostMapping("/decrease")
    public String decrease(@RequestParam("productId") Long productId,
                           @RequestParam("count") Integer count) {
        stockService.decrease(productId, count);
        return "ok";
    }
}
java
@Service
public class StockService {

    @Transactional(rollbackFor = Exception.class)
    public void decrease(Long productId, Integer count) {
        // 这里演示本地事务,实际项目中应执行 update stock set count = count - ? where product_id = ?
        if (count <= 0) {
            throw new RuntimeException("库存扣减数量非法");
        }
    }
}

5.7 下游账户服务示例

java
@RestController
@RequestMapping("/account")
public class AccountController {

    @Resource
    private AccountService accountService;

    @PostMapping("/decrease")
    public String decrease(@RequestParam("userId") Long userId,
                           @RequestParam("amount") BigDecimal amount) {
        accountService.decrease(userId, amount);
        return "ok";
    }
}
java
@Service
public class AccountService {

    @Transactional(rollbackFor = Exception.class)
    public void decrease(Long userId, BigDecimal amount) {
        // 这里演示本地事务,实际项目中应执行余额扣减 SQL
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new RuntimeException("扣减金额非法");
        }
    }
}

5.8 DataSource 代理配置示例

AT 模式中,Seata 需要代理数据源,才能拦截 SQL 并记录 undo_log

java
@Configuration
public class DataSourceProxyConfig {

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

    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        // 用 Seata 的 DataSourceProxy 包装原始数据源
        return new DataSourceProxy(dataSource);
    }
}

5.9 AT 模式常见限制

AT 模式并不是万能的,常见限制包括:

  • 不适合长事务
  • 不适合复杂存储过程
  • 不适合对同一行长期持锁的场景
  • 批量复杂 SQL 需要格外小心

5.10 AT 模式优缺点

优点:

  • 使用简单
  • 接入成本低
  • 适合大多数 CRUD 型业务

缺点:

  • 依赖本地数据库支持
  • 对复杂 SQL、批量更新、长事务不友好

5.11 AT 模式适用场景

  • 电商下单
  • 库存扣减
  • 支付记账
  • 常规后台管理系统

六、TCC、SAGA、XA 模式

6.1 TCC 模式

TCC 是:

  • Try
  • Confirm
  • Cancel

6.1.1 TCC 特点

  • 侵入性强
  • 控制粒度细
  • 一致性强

6.1.2 TCC 示例思路

text
Try: 预留库存 / 预冻结余额
Confirm: 正式扣减
Cancel: 回滚预留资源

6.1.3 TCC 代码示例

java
@LocalTCC
public interface AccountTccAction {

    @TwoPhaseBusinessAction(name = "decreaseAccount", commitMethod = "commit", rollbackMethod = "rollback")
    boolean prepare(BusinessActionContext actionContext,
                    @BusinessActionContextParameter(paramName = "userId") Long userId,
                    @BusinessActionContextParameter(paramName = "amount") BigDecimal amount);

    boolean commit(BusinessActionContext actionContext);

    boolean rollback(BusinessActionContext actionContext);
}
java
@Component
public class AccountTccActionImpl implements AccountTccAction {

    @Override
    public boolean prepare(BusinessActionContext actionContext, Long userId, BigDecimal amount) {
        // Try 阶段:预冻结余额
        return true;
    }

    @Override
    public boolean commit(BusinessActionContext actionContext) {
        // Confirm 阶段:正式扣减余额
        return true;
    }

    @Override
    public boolean rollback(BusinessActionContext actionContext) {
        // Cancel 阶段:释放冻结余额
        return true;
    }
}

6.2 XA 模式

XA 是标准 2PC 模式。

特点:

  • 强一致性
  • 数据库原生事务支持较强
  • 性能和锁资源占用较重

6.3 SAGA 模式

SAGA 更适合长事务和状态机编排场景。

特点:

  • 通过补偿事务回滚
  • 更适合长链路业务

6.3.1 SAGA 思路示意

text
创建订单 -> 扣减库存 -> 扣减余额
如果余额扣减失败 -> 补偿库存 -> 补偿订单状态

6.4 四种模式对比

模式一致性开发成本性能适用场景
AT较高较好常规业务
TCC核心资金类业务
SAGA最终一致长事务、复杂编排
XA强一致一般强一致数据库事务

6.5 模式选择建议

  • 常规业务优先 AT
  • 资金类高一致场景可考虑 TCC
  • 长流程业务可考虑 SAGA
  • 数据库强一致诉求高时可考虑 XA

七、Spring Cloud Alibaba 整合 Seata

7.1 引入依赖

xml
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <!-- Seata 集成依赖 -->
  <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

7.2 应用配置示例

yaml
seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: order_tx_group
  service:
    vgroup-mapping:
      # 事务组映射到 Seata 默认事务分组
      order_tx_group: default
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: dev-namespace-id
      application: seata-server
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: dev-namespace-id

7.3 Spring Boot 完整配置示例

yaml
server:
  port: 8088

spring:
  application:
    name: order-service
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/order_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        server-addr: 127.0.0.1:8848

seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: order_tx_group
  data-source-proxy-mode: AT
  service:
    vgroup-mapping:
      order_tx_group: default
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: dev-namespace-id
      application: seata-server
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: dev-namespace-id

7.4 数据源代理说明

Seata AT 模式通常需要代理数据源。

yaml
seata:
  data-source-proxy-mode: AT

7.5 全局事务注解说明

java
@GlobalTransactional(name = "order-create-tx", rollbackFor = Exception.class)

说明:

  • name:全局事务名
  • rollbackFor:指定哪些异常触发回滚

7.6 XID 查询示例

java
@RestController
@RequestMapping("/xid")
public class XidController {

    @GetMapping("/current")
    public String currentXid() {
        // 查看当前线程中的全局事务 XID
        return RootContext.getXID();
    }
}

7.7 一套完整业务链路

text
order-service 开启 @GlobalTransactional
 -> stock-service 扣减库存
 -> account-service 扣减余额
 -> 任一步失败则 Seata 全局回滚

重点:Spring Cloud Alibaba 项目里,Seata 一般会和 Nacos、OpenFeign 一起使用。

八、常见问题与故障排查

8.1 全局事务不回滚怎么办

重点排查:

  1. 是否加了 @GlobalTransactional
  2. rollbackFor 是否覆盖异常类型
  3. 下游异常是否被吃掉
  4. XID 是否传播成功
  5. undo_log 表是否存在

8.2 Feign 调用后事务不生效怎么办

  • 检查 Feign 是否正确传播 XID
  • 检查下游服务是否接入 Seata
  • 检查事务组配置是否一致

8.3 TCC 模式为什么更复杂

  • 需要自己实现 Try / Confirm / Cancel
  • 需要保证幂等
  • 需要处理悬挂、空回滚等问题

8.4 undo_log 有什么作用

  • 记录回滚快照
  • 帮助 AT 模式生成反向 SQL

8.5 Seata 和本地事务怎么配合

  • 局部服务内部依然使用本地事务
  • Seata 管理的是全局事务协调

8.6 常见故障点

  • 注册中心配置错误
  • 配置中心配置错误
  • 事务组映射错误
  • 数据源没有代理
  • 数据库表缺失
  • XID 没有透传
  • 异常被业务代码吞掉

九、面试重点

9.1 高频问题

  1. 什么是分布式事务
  2. Seata 的 TC、TM、RM 分别是什么
  3. AT 模式原理是什么
  4. 为什么需要 undo_log
  5. AT、TCC、SAGA、XA 的区别
  6. Seata 和 Spring Cloud Alibaba 怎么整合
  7. XID 是什么,如何传播
  8. 为什么 AT 模式需要数据源代理

9.2 高频结论

  • AT 模式最常用
  • TCC 控制最精细,但开发成本高
  • SAGA 更适合长事务
  • XA 更偏强一致,但性能压力更大

十、最佳实践

10.1 模式选择建议

  • 普通业务优先 AT
  • 强一致资金场景优先 TCC
  • 长事务场景考虑 SAGA

10.2 开发建议

  • 异常不要随意吞掉
  • 全局事务范围不要过大
  • 避免长事务
  • 关键链路做好幂等

10.3 生产建议

  • Seata Server 做高可用部署
  • 注册中心和配置中心统一接入 Nacos
  • 监控事务失败率和回滚率
  • 定期检查 undo_log
  • 控制全局事务粒度,避免长事务
  • 下游服务异常不要吞掉

10.4 总结

Seata 是 Spring Cloud Alibaba 体系里分布式事务的重要组件。

  • 入门阶段要掌握分布式事务基本概念
  • 进阶阶段要掌握 AT 模式与 Spring Cloud Alibaba 整合
  • 高阶阶段要掌握 TCC、SAGA、XA 与生产实践