Skip to content

Seata 详解

Seata(Simple Extensible Autonomous Transaction Architecture)是阿里巴巴开源的分布式事务解决方案,前身为 Fescar,2019 年 1 月正式开源并更名为 Seata。它为微服务架构提供高性能、易用的分布式事务服务。


目录

  1. 为什么需要 Seata
  2. 核心概念与架构
  3. AT 模式(最常用)
  4. TCC 模式
  5. Saga 模式
  6. XA 模式
  7. 四种模式对比
  8. 核心配置
  9. 实战配置
  10. Seata Server 部署
  11. 面试要点

1. 为什么需要 Seata

1.1 分布式事务问题场景

在微服务架构中,一个业务操作往往需要跨越多个服务、多个数据库。以电商下单为例:

用户下单流程:

  订单服务                库存服务                账户服务
  (order_db)             (storage_db)           (account_db)
     |                       |                       |
     |--- 1. 创建订单 -------|                       |
     |                       |                       |
     |----------------------- 2. 扣减库存 -----------|
     |                       |                       |
     |----------------------------------------------- 3. 扣减余额
     |                       |                       |

问题:如果扣减库存成功,但扣减余额失败(例如余额不足),此时:

  • 订单已创建(不可回滚)
  • 库存已扣减(不可回滚)
  • 余额未扣减

结果:数据不一致。库存少了,但钱没扣到,这是严重的业务故障。

1.2 没有分布式事务的后果

场景订单服务库存服务账户服务结果
正常成功成功成功数据一致
异常1成功成功失败库存扣了,钱没扣,数据不一致
异常2成功失败-订单已创建,但库存没扣,数据不一致
异常3失败--无影响

1.3 Seata 的解决方案

Seata 提供了一套完整的分布式事务协调机制,确保跨服务、跨数据库的操作要么全部成功,要么全部回滚,保证数据最终一致性。

Seata 协调下的分布式事务:

   TM(订单服务)          RM(库存服务)          RM(账户服务)
       |                     |                     |
       |-- 开启全局事务 -----|                     |
       |                     |                     |
       |-- 调用扣库存 ------->|                     |
       |                     |-- 执行+UNDO_LOG --> |
       |                     |-- 返回成功 --------->|
       |                     |                     |
       |-- 调用扣余额 -------|--------------------->|
       |                     |                     |-- 执行+UNDO_LOG
       |                     |                     |-- 返回失败!
       |                     |                     |
       |-- TC 检测到失败,通知所有 RM 回滚 --------|
       |                     |                     |
       |                     |<-- 根据 UNDO_LOG 回滚
       |                     |<--------------------|-- 根据 UNDO_LOG 回滚
       |                     |                     |
       |-- 全局事务回滚完成                         |

2. 核心概念与架构

2.1 三大角色

Seata 定义了三个核心角色,各司其职:

角色全称职责
TCTransaction Coordinator(事务协调器)维护全局事务和分支事务的状态,驱动全局事务的提交或回滚。独立部署的 Seata Server。
TMTransaction Manager(事务管理器)定义全局事务的范围:开启全局事务、提交或回滚全局事务。通常由发起方服务担任。
RMResource Manager(资源管理器)管理分支事务的资源,向 TC 注册分支事务、报告分支事务状态,驱动分支事务的提交或回滚。每个参与方服务都是 RM。

2.2 架构图

                          +-------------------+
                          |                   |
                          |   TC (Seata Server)|
                          |   事务协调器        |
                          |   (独立部署)        |
                          |                   |
                          +----+---------+----+
                               ^         ^
                               |         |
              注册分支/报告状态 |         | 注册分支/报告状态
                               |         |
          +--------------------+         +--------------------+
          |                    |         |                    |
          v                    |         |                    v
  +-------+--------+           |         |           +--------+-------+
  |                |           |         |           |                |
  |  TM (订单服务)  |-----------+         +-----------|  RM (账户服务)  |
  |  事务管理器     |  RPC 调用                        |  资源管理器     |
  |                |--------------------------------->|                |
  +----------------+                                 +----------------+
          |
          | RPC 调用
          v
  +----------------+
  |                |
  |  RM (库存服务)  |
  |  资源管理器     |
  |                |
  +----------------+

2.3 事务执行流程

1. TM 向 TC 申请开启一个全局事务,TC 返回一个全局事务 ID(XID)
2. XID 在微服务调用链中传播(通过 Feign 拦截器 / Dubbo Filter)
3. 每个 RM 向 TC 注册分支事务,TC 返回分支事务 ID(Branch ID)
4. 每个 RM 执行本地事务(业务 SQL),并记录 UNDO_LOG
5. 每个 RM 向 TC 报告分支事务状态(成功/失败)
6. TM 根据所有 RM 的执行结果,向 TC 发起全局提交或全局回滚
7. TC 通知所有 RM 进行分支事务的提交或回滚

3. AT 模式(最常用)

3.1 概述

AT 模式(Automatic Transaction)是 Seata 默认的分布式事务模式,基于两阶段提交协议,通过自动生成反向补偿 SQL 实现回滚,对业务代码零侵入

核心思想:在业务 SQL 执行前后,自动记录数据快照(UNDO_LOG),回滚时根据快照生成反向 SQL 进行补偿。

3.2 两阶段流程

一阶段:执行 + 记录 UNDO_LOG

一阶段流程(每个 RM 独立执行):

  +------------------+
  | 业务 SQL 执行前   |
  | SELECT 获取前镜像  |  <-- 查询当前数据作为 before_image
  +--------+---------+
           |
           v
  +------------------+
  | 业务 SQL 执行     |  <-- 执行 INSERT/UPDATE/DELETE
  +--------+---------+
           |
           v
  +------------------+
  | 业务 SQL 执行后   |
  | SELECT 获取后镜像  |  <-- 查询变更后数据作为 after_image
  +--------+---------+
           |
           v
  +------------------+
  | 写入 UNDO_LOG    |  <-- 将 before_image + after_image 写入 UNDO_LOG 表
  +------------------+

关键点:一阶段所有操作在一个本地事务中完成,保证原子性。

二阶段:提交或回滚

二阶段 - 提交(所有 RM 均成功):

  +------------------+
  | TC 通知所有 RM    |
  | 全局提交          |
  +--------+---------+
           |
           v
  +------------------+
  | RM 异步删除       |
  | UNDO_LOG 记录     |  <-- 仅删除 UNDO_LOG,业务数据已持久化
  +------------------+


二阶段 - 回滚(任一 RM 失败):

  +------------------+
  | TC 通知所有 RM    |
  | 全局回滚          |
  +--------+---------+
           |
           v
  +------------------+
  | RM 读取 UNDO_LOG |  <-- 获取 before_image 和 after_image
  +--------+---------+
           |
           v
  +------------------+
  | 校验数据一致性    |  <-- 对比当前数据与 after_image,防止脏写
  +--------+---------+
           |
           v
  +------------------+
  | 执行反向补偿 SQL  |  <-- 根据 before_image 生成反向 UPDATE/DELETE/INSERT
  +--------+---------+
           |
           v
  +------------------+
  | 删除 UNDO_LOG    |
  +------------------+

3.3 完整流程图

时间线 ──────────────────────────────────────────────────────────────>

  TM                    TC                    RM-A               RM-B
  (订单服务)           (Seata Server)        (库存服务)          (账户服务)
   |                      |                     |                  |
   |--1. 开启全局事务 ---->|                     |                  |
   |<-- 返回 XID ---------|                     |                  |
   |                      |                     |                  |
   |--2. 调用 RM-A ------>|                     |                  |
   |                      |<--3. 注册分支事务 ---|                  |
   |                      |-- 返回 BranchId --->|                  |
   |                      |                     |                  |
   |                      |                     |--4. 执行本地事务 |
   |                      |                     |   (SQL + UNDO_LOG)
   |                      |<--5. 报告成功 -------|                  |
   |                      |                     |                  |
   |--6. 调用 RM-B ------>|                     |                  |
   |                      |<--7. 注册分支事务 ---|------------------|
   |                      |-- 返回 BranchId --->|                  |
   |                      |                     |                  |
   |                      |                     |   --8. 执行本地事务
   |                      |                     |   (SQL + UNDO_LOG)
   |                      |                     |<--9. 报告失败!---|
   |                      |                     |                  |
   |<--10. 感知失败 ------|                     |                  |
   |--11. 发起回滚 ------>|                     |                  |
   |                      |--12. 通知 RM-A 回滚->|                  |
   |                      |--13. 通知 RM-B 回滚-|----------------->|
   |                      |                     |                  |
   |                      |                     |--14. 反向补偿 ----|
   |                      |<--15. 回滚完成 ------|                  |
   |                      |                     |--16. 反向补偿 ----|
   |                      |<--17. 回滚完成 ------|------------------|
   |                      |                     |                  |
   |<--18. 全局事务结束 ---|                     |                  |

3.4 隔离性保证

读隔离(Read Committed)

Seata AT 模式默认实现 读已提交(Read Committed) 隔离级别。

  • SELECT FOR UPDATE 语句会申请全局锁,如果全局锁被其他事务持有,则等待直到锁释放。
  • 普通 SELECT 语句直接读取本地数据,不涉及全局锁(默认读已提交)。

写隔离(全局锁防脏写)

写隔离流程:

  事务 A                     TC                    事务 B
   (正在执行)               (Seata)                (并发执行)
      |                       |                       |
      |--1. 执行业务 SQL -----|                       |
      |--2. 获取全局锁 ------->|                       |
      |<-- 获取成功 ----------|                       |
      |                       |                       |
      |                       |<--3. 尝试获取全局锁 ---|
      |                       |-- 锁已被 A 持有        |
      |                       |-- 等待/重试... ------->|
      |                       |                       |
      |--4. 提交/回滚 --------|                       |
      |--5. 释放全局锁 ------->|                       |
      |                       |--6. 通知 B 获取锁 ---->|
      |                       |                       |
      |                       |                       |--7. 执行业务 SQL

全局锁机制:在事务提交前,必须获取对应数据行的全局锁,防止其他事务并发修改同一数据。

3.5 UNDO_LOG 表结构

sql
-- 每个业务数据库都需要创建此表
CREATE TABLE `undo_log` (
  `id`            BIGINT(20)   NOT NULL AUTO_INCREMENT COMMENT '主键',
  `branch_id`     BIGINT(20)   NOT NULL COMMENT '分支事务 ID',
  `xid`           VARCHAR(128) NOT NULL COMMENT '全局事务 ID',
  `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log 上下文',
  `rollback_info` LONGBLOB     NOT NULL COMMENT '回滚日志(before_image + after_image 序列化 JSON)',
  `log_status`    INT(11)      NOT NULL COMMENT '日志状态:0-正常,1-已删除',
  `log_created`   DATETIME     NOT NULL COMMENT '创建时间',
  `log_modified`  DATETIME     NOT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AT 模式 UNDO_LOG 表';

3.6 AT 模式优缺点

优点缺点
对业务代码零侵入,仅需添加 @GlobalTransactional 注解需要每个业务数据库创建 UNDO_LOG 表
自动生成回滚 SQL,开发成本低一阶段本地事务和二阶段回滚有短暂时间差,存在中间状态
性能较好,一阶段即提交,二阶段异步清理依赖数据库的本地事务能力(需要支持 ACID)
支持各种 SQL(INSERT/UPDATE/DELETE)不支持跨数据库的 JOIN 操作

4. TCC 模式

4.1 概述

TCC(Try-Confirm-Cancel)是一种侵入式的分布式事务模式,需要开发者手动实现三个阶段的逻辑。

核心思想:将业务操作拆分为 Try(预留资源)、Confirm(确认提交)、Cancel(取消回滚)三个阶段,每个阶段都需要开发者手动编码实现

4.2 三阶段流程

TCC 事务执行流程:

  阶段 1: Try(预留资源)
  +--------------------------------------------------+
  |  账户服务:冻结余额(不直接扣减)                    |
  |  库存服务:冻结库存(不直接扣减)                    |
  |  订单服务:创建"预订单"(状态为待确认)               |
  +--------------------------------------------------+
                          |
                          v
          +-------------------------------+
          | 所有 Try 成功?                |
          +----+---------------------+----+
               | YES                  | NO
               v                      v
  +------------------------+  +------------------------+
  | 阶段 2: Confirm(确认) |  | 阶段 2: Cancel(取消)  |
  +------------------------+  +------------------------+
  | 账户:实际扣减冻结余额   |  | 账户:解冻余额           |
  | 库存:实际扣减冻结库存   |  | 库存:解冻库存           |
  | 订单:状态改为已确认     |  | 订单:状态改为已取消      |
  +------------------------+  +------------------------+

4.3 与 AT 模式的区别

维度AT 模式TCC 模式
回滚方式自动生成反向 SQL手动编写 Cancel 逻辑
代码侵入性无(仅需注解)高(需实现 Try/Confirm/Cancel)
性能更好(无 UNDO_LOG 开销)
开发成本
灵活性受限于 SQL 自动补偿极高,可自定义任意补偿逻辑
资源锁定全局锁业务层面预留资源

4.4 代码示例

java
// 账户服务 - TCC 接口定义
@LocalTCC
public interface AccountTccService {

    /**
     * Try 阶段:冻结余额
     * @TwoPhaseBusinessAction 标注 TCC 两阶段方法
     *   - name: TCC 资源名称,全局唯一
     *   - commitMethod: Confirm 方法名
     *   - rollbackMethod: Cancel 方法名
     */
    @TwoPhaseBusinessAction(
        name = "account-deduct",
        commitMethod = "deductConfirm",
        rollbackMethod = "deductCancel"
    )
    boolean deductTry(
        @BusinessActionContextParameter(paramName = "userId") String userId,
        @BusinessActionContextParameter(paramName = "amount") BigDecimal amount
    );

    /**
     * Confirm 阶段:实际扣减
     */
    boolean deductConfirm(BusinessActionContext context);

    /**
     * Cancel 阶段:解冻余额
     */
    boolean deductCancel(BusinessActionContext context);
}
java
// 账户服务 - TCC 接口实现
@Service
public class AccountTccServiceImpl implements AccountTccService {

    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private FreezeLogMapper freezeLogMapper;

    @Override
    @Transactional
    public boolean deductTry(String userId, BigDecimal amount) {
        // Try:冻结余额,不实际扣减
        // 1. 检查可用余额是否足够
        Account account = accountMapper.selectByUserId(userId);
        if (account.getAvailableAmount().compareTo(amount) < 0) {
            throw new BusinessException("余额不足");
        }
        // 2. 冻结金额(可用余额减少,冻结金额增加)
        accountMapper.freezeAmount(userId, amount);
        // 3. 记录冻结日志
        String xid = RootContext.getXID();
        freezeLogMapper.insert(new FreezeLog(xid, userId, amount));
        return true;
    }

    @Override
    @Transactional
    public boolean deductConfirm(BusinessActionContext context) {
        // Confirm:实际扣减冻结金额
        String userId = (String) context.getActionContext("userId");
        BigDecimal amount = new BigDecimal((String) context.getActionContext("amount"));
        // 1. 实际扣减冻结金额
        accountMapper.confirmDeduct(userId, amount);
        // 2. 删除冻结日志
        freezeLogMapper.deleteByXid(context.getXid());
        return true;
    }

    @Override
    @Transactional
    public boolean deductCancel(BusinessActionContext context) {
        // Cancel:解冻余额,恢复可用余额
        String userId = (String) context.getActionContext("userId");
        BigDecimal amount = new BigDecimal((String) context.getActionContext("amount"));
        // 1. 解冻金额恢复
        accountMapper.unfreezeAmount(userId, amount);
        // 2. 删除冻结日志
        freezeLogMapper.deleteByXid(context.getXid());
        return true;
    }
}
java
// 业务调用方 - 使用 @GlobalTransactional
@Service
public class OrderService {

    @Autowired
    private AccountTccService accountTccService;

    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public void createOrder(OrderDTO orderDTO) {
        // 1. 创建订单
        orderMapper.insert(orderDTO);

        // 2. 扣减库存(TCC)
        storageTccService.deductTry(orderDTO.getProductId(), orderDTO.getQuantity());

        // 3. 扣减余额(TCC)
        accountTccService.deductTry(orderDTO.getUserId(), orderDTO.getAmount());

        // 任何一步抛出异常,TC 将自动调用各服务的 Cancel 方法
    }
}

4.5 适用场景

  • 高性能场景:如账户余额扣减、积分扣减,需要高并发、低延迟
  • 需要自定义补偿逻辑:如调用第三方接口,需要发送取消请求
  • 资源预留型业务:如秒杀、抢购,需要先冻结资源再确认

5. Saga 模式

5.1 概述

Saga 模式是 Seata 提供的长事务解决方案。在 Saga 模式中,业务流程中的每个参与者都提交本地事务,当某个参与者失败时,则按相反顺序调用之前成功参与者的补偿操作。

核心思想:一阶段正向服务依次调用并提交,失败时反向调用补偿服务。

5.2 执行流程

Saga 事务执行流程:

  正向执行:
  +-------------+     +-------------+     +-------------+
  | 服务 A       | --> | 服务 B       | --> | 服务 C       |
  | (正向操作)   |     | (正向操作)   |     | (正向操作)   |
  | 提交本地事务  |     | 提交本地事务  |     | 提交本地事务  |
  +-------------+     +-------------+     +-------------+
                                                    |
                                                    | 失败!
                                                    v
  反向补偿:
  +-------------+     +-------------+     +-------------+
  | 服务 A       | <-- | 服务 B       | <-- | 服务 C       |
  | (补偿操作)   |     | (补偿操作)   |     | (补偿操作)   |
  +-------------+     +-------------+     +-------------+

5.3 代码示例

java
// Saga 服务定义(基于状态机)
// 方式一:使用 JSON 状态机 DSL 定义
json
{
  "Name": "create-order-saga",
  "StartState": "CreateOrder",
  "States": {
    "CreateOrder": {
      "Type": "ServiceTask",
      "ServiceName": "orderService.create",
      "CompensateState": "CancelOrder",
      "Next": "DeductStorage"
    },
    "DeductStorage": {
      "Type": "ServiceTask",
      "ServiceName": "storageService.deduct",
      "CompensateState": "RestoreStorage",
      "Next": "DeductBalance"
    },
    "DeductBalance": {
      "Type": "ServiceTask",
      "ServiceName": "accountService.deduct",
      "CompensateState": "RestoreBalance",
      "Next": "Success"
    },
    "CancelOrder": {
      "Type": "ServiceTask",
      "ServiceName": "orderService.cancel",
      "Next": "Fail"
    },
    "RestoreStorage": {
      "Type": "ServiceTask",
      "ServiceName": "storageService.restore",
      "Next": "CancelOrder"
    },
    "RestoreBalance": {
      "Type": "ServiceTask",
      "ServiceName": "accountService.restore",
      "Next": "RestoreStorage"
    },
    "Success": {
      "Type": "Succeed"
    },
    "Fail": {
      "Type": "Fail"
    }
  }
}
java
// 方式二:使用 @GlobalTransactional 注解(需要配合状态机引擎)
@Service
public class OrderSagaService {

    @GlobalTransactional
    public void createOrder(OrderDTO orderDTO) {
        // 正向操作:每个服务独立提交本地事务
        orderService.create(orderDTO);           // 补偿:orderService.cancel(orderId)
        storageService.deduct(orderDTO);         // 补偿:storageService.restore(orderDTO)
        accountService.deduct(orderDTO);         // 补偿:accountService.restore(orderDTO)
    }
}

5.4 适用场景

  • 老系统改造:无法修改原有代码,只能通过补偿接口实现回滚
  • 长事务场景:如出行订票(机票 + 酒店 + 租车),事务可能持续数分钟甚至数小时
  • 不支持 ACID 的数据库:如 MongoDB、Redis 等 NoSQL 数据库
  • 调用第三方服务:无法控制第三方服务的回滚,只能通过补偿接口处理

5.5 Saga 模式特点

优点缺点
一阶段直接提交,无锁等待,性能高需手动编写补偿逻辑
适合长事务,不会长时间占用资源可能出现中间状态(数据暂时不一致)
适合异构数据库环境补偿操作必须实现幂等
适合调用第三方服务无法保证隔离性

6. XA 模式

6.1 概述

XA 模式基于 X/Open DTP 模型,使用 XA 协议实现强一致性的分布式事务。Seata 对数据库的 XA 接口进行了封装,使用上与 AT 模式类似。

核心思想:利用数据库自身的 XA 事务支持,通过两阶段提交(2PC)协议保证强一致性。

6.2 两阶段流程

XA 模式两阶段提交:

  一阶段 - Prepare(预提交):
  +--------------------------------------------------+
  |  TM 向所有 RM 发送 Prepare 指令                    |
  |  RM 执行 SQL,但不提交(数据被锁定)                  |
  |  RM 返回 Prepare 结果(Ready / Fail)               |
  +--------------------------------------------------+
                          |
                          v
          +-------------------------------+
          | 所有 RM 返回 Ready?           |
          +----+---------------------+----+
               | YES                  | NO
               v                      v
  +------------------------+  +------------------------+
  | 二阶段 - Commit(提交) |  | 二阶段 - Rollback(回滚)|
  +------------------------+  +------------------------+
  | TM 通知所有 RM 提交     |  | TM 通知所有 RM 回滚     |
  | RM 执行 COMMIT         |  | RM 执行 ROLLBACK       |
  | 释放锁资源             |  | 释放锁资源             |
  +------------------------+  +------------------------+

6.3 代码示例

java
// XA 模式配置
@Configuration
public class SeataXaConfig {

    @Bean
    public DataSourceProxy dataSource(DruidDataSource druidDataSource) {
        // XA 模式使用 DataSourceProxyXA 代理数据源
        return new DataSourceProxyXA(druidDataSource);
    }
}
yaml
# application.yml - XA 模式配置
seata:
  data-source-proxy-mode: XA  # 开启 XA 模式
  tx-service-group: my_tx_group
  service:
    vgroup-mapping:
      my_tx_group: default
java
// 业务代码与 AT 模式完全相同,仅需 @GlobalTransactional
@Service
public class OrderService {

    @GlobalTransactional
    public void createOrder(OrderDTO orderDTO) {
        orderMapper.insert(orderDTO);
        storageService.deduct(orderDTO.getProductId(), orderDTO.getQuantity());
        accountService.deduct(orderDTO.getUserId(), orderDTO.getAmount());
    }
}

6.4 XA 模式 vs AT 模式

维度XA 模式AT 模式
一致性强一致性(Prepare 期间数据锁定)最终一致性(一阶段提交后释放锁)
性能差(Prepare 期间锁定资源,事务持续时间长)好(一阶段即提交,锁持有时间短)
数据库支持需要数据库支持 XA 协议需要支持 ACID 事务的数据库
回滚方式数据库原生 ROLLBACK根据 UNDO_LOG 反向补偿
代码侵入无(注解驱动)无(注解驱动)
适用场景对一致性要求极高的金融场景大部分互联网业务场景

6.5 适用场景

  • 金融核心系统:资金转账、账务处理,不允许任何中间不一致状态
  • 对一致性要求极高的场景:强一致性 > 性能
  • 数据库天然支持 XA:MySQL、Oracle、PostgreSQL 等

7. 四种模式对比

7.1 综合对比表

维度AT 模式TCC 模式Saga 模式XA 模式
一致性最终一致性最终一致性最终一致性强一致性
隔离性读已提交 + 全局锁写隔离业务层隔离无隔离保证持久隔离(Prepare 锁定)
性能最好
开发成本(零侵入)高(需手写 Try/Confirm/Cancel)中(需手写补偿接口)低(零侵入)
代码侵入性
回滚方式自动生成反向 SQL手动 Cancel 逻辑手动补偿操作数据库原生 ROLLBACK
数据库要求支持 ACID 的关系型数据库无特殊要求无特殊要求支持 XA 协议的关系型数据库
UNDO_LOG 表需要不需要不需要不需要
资源锁定全局锁业务层预留资源无锁数据库行锁(Prepare 期间)
适用场景大部分互联网业务高性能、高并发场景长事务、老系统改造强一致性金融场景
幂等要求框架自动处理Confirm/Cancel 需幂等补偿操作需幂等框架自动处理
空回滚处理框架自动处理需手动处理 Cancel需手动处理框架自动处理

7.2 选型建议

选型决策树:

  需要强一致性?
    ├── YES → XA 模式
    └── NO
        ├── 长事务(> 分钟级)?
        │   ├── YES → Saga 模式
        │   └── NO
        │       ├── 需要极致性能或自定义补偿?
        │       │   ├── YES → TCC 模式
        │       │   └── NO → AT 模式(默认推荐)

8. 核心配置

8.1 Seata Client 完整配置

yaml
# application.yml - Seata Client 配置
seata:
  # 是否开启 Seata 自动配置
  enabled: true
  
  # 应用名称(对应 Seata Server 中的事务分组映射)
  application-id: ${spring.application.name}
  
  # 事务分组(重要!用于匹配 TC 集群)
  tx-service-group: my_tx_group
  
  # 服务配置:事务分组 → 集群名称映射
  service:
    vgroup-mapping:
      # key: 事务分组名称, value: TC 集群名称(Seata Server 的 registry 集群名)
      my_tx_group: default
    # 禁用全局事务分组配置
    disable-global-transaction: false
    # 全局事务超时(毫秒)默认 60 秒
    grouplist:
      default: 127.0.0.1:8091
  
  # 注册中心配置
  registry:
    # 注册中心类型:nacos / eureka / redis / zk / consul / etcd3 / sofa
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: ""  # Nacos 命名空间 ID
      cluster: default
      username: nacos
      password: nacos
  
  # 配置中心配置
  config:
    # 配置中心类型:nacos / consul / apollo / zk / etcd3
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: ""
      data-id: seata.properties
      username: nacos
      password: nacos
  
  # 存储模式(仅 file 模式时有效,db/redis 模式在 Server 端配置)
  store:
    # file / db / redis
    mode: file
    file:
      dir: sessionStore
      max-branch-session-size: 16384
      max-global-session-size: 512
      file-write-buffer-cache-size: 16384
      session-reload-read-size: 100
      flush-disk-mode: async
  
  # 传输配置
  transport:
    type: TCP
    server: NIO
    heartbeat: true
    enable-client-batch-send-request: true
    thread-factory:
      boss-thread-prefix: NettyBoss
      worker-thread-prefix: NettyServerNIOWorker
      server-executor-thread-prefix: NettyServerBizHandler
      share-boss-worker: false
      client-selector-thread-prefix: NettyClientSelector
      client-selector-thread-size: 1
      client-worker-thread-prefix: NettyClientWorkerThread
      boss-thread-size: 1
      worker-thread-size: default
    shutdown:
      wait: 3
  
  # 客户端参数
  client:
    rm:
      # 异步提交缓存队列大小
      async-commit-buffer-limit: 10000
      # 一阶段结果上报重试次数
      report-retry-count: 5
      # 表元数据校验
      table-meta-check-enable: false
      # 上报重试间隔(毫秒)
      report-success-enable: false
      # Saga 分支注册重试次数
      saga-branch-register-enable: false
      # SQL 解析器类型
      sql-parser-type: druid
      # 锁重试间隔(毫秒)
      lock:
        retry-interval: 10
        retry-times: 30
        retry-policy-branch-rollback-on-conflict: true
    tm:
      # 全局事务提交重试次数
      commit-retry-count: 5
      # 全局事务回滚重试次数
      rollback-retry-count: 5
      # 一阶段方法是否降级
      degrade-check: false
      degrade-check-period: 2000
      degrade-check-allow-times: 10
    undo:
      # UNDO_LOG 存储方式
      data-validation: true
      # 日志序列化方式
      log-serialization: jackson
      # UNDO_LOG 表名
      log-table: undo_log
      # 仅当 UPDATE 前后镜像不同时才记录 UNDO_LOG
      only-care-update-columns: true
    load-balance:
      type: XID
  # 日志配置
  log:
    exception-rate: 100

8.2 数据源代理配置

java
// 方式一:手动配置数据源代理(推荐,更灵活)
@Configuration
public class SeataDataSourceConfig {

    /**
     * 使用 Seata 包装原始数据源
     * Seata 通过 DataSourceProxy 拦截 SQL 执行,自动记录 UNDO_LOG
     */
    @Bean
    @Primary
    public DataSource dataSource(DruidDataSource druidDataSource) {
        // AT 模式使用 DataSourceProxy
        return new DataSourceProxy(druidDataSource);
        
        // XA 模式使用 DataSourceProxyXA
        // return new DataSourceProxyXA(druidDataSource);
    }
}
yaml
# 方式二:使用自动代理(Spring Boot 自动配置)
seata:
  data-source-proxy-mode: AT  # AT / XA

8.3 XID 传播配置

java
// Feign 拦截器 - 自动传播 XID
@Component
public class SeataFeignInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {
        String xid = RootContext.getXID();
        if (StringUtils.hasText(xid)) {
            template.header(RootContext.KEY_XID, xid);
        }
    }
}
java
// Filter 接收 XID
@Component
public class SeataFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String xid = httpRequest.getHeader(RootContext.KEY_XID);
        if (StringUtils.hasText(xid)) {
            RootContext.bind(xid);
        }
        try {
            chain.doFilter(request, response);
        } finally {
            RootContext.unbind();
        }
    }
}

9. 实战配置

9.1 环境准备

依赖版本:
  - Spring Boot: 2.3.x+
  - Spring Cloud: Hoxton.SR9+
  - Spring Cloud Alibaba: 2.2.6.RELEASE+
  - Seata: 1.4.2+
  - Nacos: 1.4.2+
  - MySQL: 5.7+

9.2 Maven 依赖

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

<!-- 排除自带的低版本 Seata -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!-- 引入指定版本的 Seata -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.4.2</version>
</dependency>

9.3 创建 UNDO_LOG 表

sql
-- 在每个业务数据库执行以下 SQL
-- 订单库 (order_db)
CREATE TABLE `undo_log` (
  `id`            BIGINT(20)   NOT NULL AUTO_INCREMENT,
  `branch_id`     BIGINT(20)   NOT NULL,
  `xid`           VARCHAR(128) 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,
  `ext`           VARCHAR(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 库存库 (storage_db)
-- (同上 SQL)

-- 账户库 (account_db)
-- (同上 SQL)

9.4 配置 registry.conf

conf
# registry.conf - Seata 注册中心配置
# 此文件放在 resources 目录下

registry {
  type = "nacos"
  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = ""
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
}

config {
  type = "nacos"
  nacos {
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = ""
    dataId = "seata.properties"
    username = "nacos"
    password = "nacos"
  }
}

9.5 配置 application.yml

yaml
# application.yml - 订单服务
spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/order_db?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource

seata:
  tx-service-group: my_tx_group
  service:
    vgroup-mapping:
      my_tx_group: default
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      application: seata-server
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
  data-source-proxy-mode: AT

9.6 数据源代理 Bean 配置

java
package com.example.order.config;

import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

@Configuration
public class SeataDataSourceConfig {

    /**
     * 原始 Druid 数据源
     * 注意:此 Bean 不需要 @Primary,SeataDataSourceProxy 设置为 @Primary
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DruidDataSource druidDataSource() {
        return new DruidDataSource();
    }

    /**
     * Seata 数据源代理
     * 设置为 @Primary,所有 MyBatis/ JPA 将使用此代理数据源
     */
    @Bean
    @Primary
    public DataSource dataSource(DruidDataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }
}

9.7 @GlobalTransactional 注解使用

java
package com.example.order.service;

import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private StorageFeignClient storageFeignClient;
    @Autowired
    private AccountFeignClient accountFeignClient;

    /**
     * @GlobalTransactional 开启全局事务
     * 
     * 属性说明:
     *   - name: 全局事务名称(可选)
     *   - timeoutMills: 超时时间(毫秒),默认 60 秒
     *   - rollbackFor: 触发回滚的异常类型
     *   - noRollbackFor: 不触发回滚的异常类型
     *   - propagation: 事务传播行为(默认 REQUIRED)
     */
    @GlobalTransactional(
        name = "create-order",
        timeoutMills = 300000,  // 5 分钟
        rollbackFor = Exception.class
    )
    public void createOrder(OrderDTO orderDTO) {
        // 1. 本地事务:创建订单
        Order order = new Order();
        order.setUserId(orderDTO.getUserId());
        order.setProductId(orderDTO.getProductId());
        order.setQuantity(orderDTO.getQuantity());
        order.setAmount(orderDTO.getAmount());
        order.setStatus("PENDING");
        orderMapper.insert(order);

        // 2. 远程调用:扣减库存
        storageFeignClient.deduct(orderDTO.getProductId(), orderDTO.getQuantity());

        // 3. 远程调用:扣减余额
        accountFeignClient.deduct(orderDTO.getUserId(), orderDTO.getAmount());

        // 4. 更新订单状态
        order.setStatus("SUCCESS");
        orderMapper.updateById(order);

        // 任何步骤抛出异常,Seata 会自动回滚所有操作
    }
}

9.8 全局事务回滚示例

java
@Service
public class OrderService {

    @GlobalTransactional
    public void createOrderWithRollback(OrderDTO orderDTO) {
        // 步骤1:创建订单 - 成功
        orderMapper.insert(order);
        log.info("订单创建成功,订单ID: {}", order.getId());

        // 步骤2:扣减库存 - 成功
        storageFeignClient.deduct(orderDTO.getProductId(), orderDTO.getQuantity());
        log.info("库存扣减成功");

        // 步骤3:扣减余额 - 失败!余额不足
        try {
            accountFeignClient.deduct(orderDTO.getUserId(), orderDTO.getAmount());
        } catch (Exception e) {
            log.error("余额扣减失败,触发全局回滚: {}", e.getMessage());
            // 抛出异常,Seata 自动回滚步骤1和步骤2
            throw new BusinessException("余额不足,订单创建失败");
        }
    }
}

/*
 * 执行结果:
 * 
 * 1. 订单表:插入的记录被自动回滚删除
 * 2. 库存表:扣减的库存被自动恢复(UNDO_LOG 反向补偿)
 * 3. 账户表:无变化(调用失败,未执行本地事务)
 * 
 * 最终:三个数据库数据一致,相当于什么都没发生
 */

9.9 Seata 执行日志示例

# 正常流程日志
2024-01-01 10:00:00.001  INFO  Begin new global transaction [192.168.1.1:8091:1234567890]
2024-01-01 10:00:00.010  INFO  Register branch transaction [192.168.1.1:8091:1234567890:1234567891]
2024-01-01 10:00:00.100  INFO  Branch commit success [192.168.1.1:8091:1234567890:1234567891]
2024-01-01 10:00:00.200  INFO  Global transaction commit [192.168.1.1:8091:1234567890]

# 回滚流程日志
2024-01-01 10:00:00.001  INFO  Begin new global transaction [192.168.1.1:8091:1234567890]
2024-01-01 10:00:00.010  INFO  Register branch transaction [192.168.1.1:8091:1234567890:1234567891]
2024-01-01 10:00:00.100  INFO  Branch commit success [192.168.1.1:8091:1234567890:1234567891]
2024-01-01 10:00:00.200  INFO  Register branch transaction [192.168.1.1:8091:1234567890:1234567892]
2024-01-01 10:00:00.300  ERROR Branch commit failed [192.168.1.1:8091:1234567890:1234567892]
2024-01-01 10:00:00.301  INFO  Global transaction rollback [192.168.1.1:8091:1234567890]
2024-01-01 10:00:00.310  INFO  Branch rollback success [192.168.1.1:8091:1234567890:1234567891]
2024-01-01 10:00:00.320  INFO  Branch rollback success [192.168.1.1:8091:1234567890:1234567892]

10. Seata Server 部署

10.1 下载与安装

bash
# 1. 下载 Seata Server
wget https://github.com/seata/seata/releases/download/v1.4.2/seata-server-1.4.2.tar.gz

# 2. 解压
tar -zxvf seata-server-1.4.2.tar.gz

# 3. 目录结构
cd seata
# ├── bin/            # 启动脚本
# │   ├── seata-server.sh
# │   └── seata-server.bat
# ├── conf/           # 配置文件
# │   ├── registry.conf     # 注册中心配置
# │   ├── file.conf         # file 模式配置(不推荐)
# │   └── logback.xml       # 日志配置
# ├── lib/            # 依赖包
# └── target/         # 启动 JAR

10.2 三种存储模式

模式一:file 模式(默认,不推荐生产)

conf
# conf/file.conf
store {
  mode = "file"
  file {
    dir = "sessionStore"
    maxBranchSessionSize = 16384
    maxGlobalSessionSize = 512
    fileWriteBufferCacheSize = 16384
    sessionReloadReadSize = 100
    flushDiskMode = async
  }
}

特点:单机模式,无高可用,数据存储在本地文件,重启数据丢失。仅适合开发测试。

模式二:db 模式(推荐生产)

sql
-- 1. 创建 Seata Server 数据库
CREATE DATABASE seata;
USE seata;

-- 2. 创建全局事务表
CREATE TABLE `global_table` (
  `xid`         VARCHAR(128) NOT NULL,
  `transaction_id` BIGINT,
  `status`      TINYINT NOT NULL,
  `application_id` VARCHAR(32),
  `transaction_service_group` VARCHAR(32),
  `transaction_name` VARCHAR(128),
  `timeout`     INT,
  `begin_time`  BIGINT,
  `application_data` VARCHAR(2000),
  `gmt_create`  DATETIME,
  `gmt_modified` DATETIME,
  PRIMARY KEY (`xid`),
  KEY `idx_status_gmt_modified` (`status`, `gmt_modified`),
  KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 3. 创建分支事务表
CREATE TABLE `branch_table` (
  `branch_id`         BIGINT NOT NULL,
  `xid`               VARCHAR(128) NOT NULL,
  `transaction_id`    BIGINT,
  `resource_group_id` VARCHAR(32),
  `resource_id`       VARCHAR(256),
  `branch_type`       VARCHAR(8),
  `status`            TINYINT,
  `client_id`         VARCHAR(64),
  `application_data`  VARCHAR(2000),
  `gmt_create`        DATETIME(6),
  `gmt_modified`      DATETIME(6),
  PRIMARY KEY (`branch_id`),
  KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 4. 创建全局锁表
CREATE TABLE `lock_table` (
  `row_key`        VARCHAR(128) NOT NULL,
  `xid`            VARCHAR(128),
  `transaction_id` BIGINT,
  `branch_id`      BIGINT NOT NULL,
  `resource_id`    VARCHAR(256),
  `table_name`     VARCHAR(32),
  `pk`             VARCHAR(36),
  `status`         TINYINT NOT NULL DEFAULT '0',
  `gmt_create`     DATETIME,
  `gmt_modified`   DATETIME,
  PRIMARY KEY (`row_key`),
  KEY `idx_branch_id` (`branch_id`),
  KEY `idx_xid_and_branch_id` (`xid`, `branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
conf
# conf/registry.conf - db 模式配置
store {
  mode = "db"
  db {
    datasource = "druid"
    dbType = "mysql"
    driverClassName = "com.mysql.cj.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true"
    user = "root"
    password = "root"
    minConn = 5
    maxConn = 100
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }
}

模式三:Nacos 模式(推荐)

在 Nacos 中创建 seata.properties 配置项(属于 SEATA_GROUP 分组),内容如下:

properties
# seata.properties - 存储在 Nacos 配置中心

# 存储模式
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=root
store.db.minConn=5
store.db.maxConn=100
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.lockTable=lock_table
store.db.queryLimit=100

# 服务配置
service.vgroupMapping.my_tx_group=default
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false

# 传输配置
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=true
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3

10.3 启动 Seata Server

bash
# Linux / Mac
cd seata/bin
./seata-server.sh -p 8091 -h 127.0.0.1 -m db

# Windows
cd seata\bin
seata-server.bat -p 8091 -h 127.0.0.1 -m db

# 参数说明:
#   -p: 端口号(默认 8091)
#   -h: 绑定 IP(默认 0.0.0.0)
#   -m: 存储模式(file / db / redis)
#   -n: Server 节点 ID(多节点部署时区分)

10.4 高可用部署

Seata 高可用架构:

                    +------------------+
                    |   Nacos 集群      |
                    |  (注册中心+配置中心)|
                    +----+--------+----+
                         ^        ^
                         |        |
          +--------------+        +--------------+
          |                                      |
          v                                      v
  +-------+--------+                    +--------+-------+
  | Seata Server 1  |                    | Seata Server 2  |
  |  192.168.1.1    |                    |  192.168.1.2    |
  |  Port: 8091     |                    |  Port: 8091     |
  +-------+--------+                    +--------+-------+
          |                                      |
          +------------------+-------------------+
                             |
                             v
                    +------------------+
                    |   MySQL 主从      |
                    |   (seata 库)     |
                    +------------------+

高可用部署步骤

bash
# 1. 在 Nacos 中配置集群信息
# 确保 seata.properties 中 store.mode=db,指向同一个 MySQL

# 2. 启动多个 Seata Server 节点
# 节点 1
./seata-server.sh -p 8091 -h 192.168.1.1 -m db -n 1

# 节点 2
./seata-server.sh -p 8091 -h 192.168.1.2 -m db -n 2

# 节点 3
./seata-server.sh -p 8091 -h 192.168.1.3 -m db -n 3

# 3. 客户端配置
# seata.service.vgroup-mapping.my_tx_group=default
# 客户端从 Nacos 获取 default 集群下所有 Seata Server 节点
# 通过 XID 负载均衡,同一事务的请求路由到同一 Server

11. 面试要点

11.1 高频面试题

Q1: 说一下 Seata AT 模式的两阶段原理?

AT 模式基于两阶段提交协议,但与传统 2PC 不同:

  • 一阶段:执行业务 SQL,同时记录 UNDO_LOG(前镜像 + 后镜像),然后直接提交本地事务,释放数据库锁。UNDO_LOG 和业务 SQL 在同一个本地事务中,保证原子性。
  • 二阶段
    • 如果全局事务成功,TC 通知各 RM 异步删除 UNDO_LOG 即可。
    • 如果全局事务失败,TC 通知各 RM 根据 UNDO_LOG 中的前镜像生成反向 SQL 进行补偿,然后删除 UNDO_LOG。

关键点:一阶段直接提交(不像传统 2PC 的 Prepare 阶段锁定资源),因此性能更好。但不保证强一致性,属于最终一致性方案。

Q2: TCC 模式和 AT 模式有什么区别?如何选择?

区别AT 模式TCC 模式
补偿方式框架自动生成反向 SQL开发者手动编写 Cancel 逻辑
代码侵入无(仅需注解)高(需实现 Try/Confirm/Cancel)
性能好(有 UNDO_LOG 开销)更好(无 UNDO_LOG,直接操作)
资源锁定全局锁业务层预留资源
灵活性受限于 SQL 反向生成极高,任意自定义补偿

选择

  • 大部分业务用 AT 模式,开发成本低,开箱即用。
  • 高性能、高并发场景(如秒杀、账户扣减)用 TCC,避免全局锁竞争。
  • 需要调用第三方接口、发送消息等复杂补偿逻辑用 TCC。

Q3: Seata 的全局锁机制是什么?为什么需要全局锁?

全局锁是 Seata AT 模式保证写隔离的核心机制。

为什么需要:AT 模式一阶段直接提交本地事务,释放数据库锁。如果两个全局事务同时修改同一行数据,可能出现脏写。

机制:在事务提交前,RM 必须向 TC 申请全局锁(锁定资源 ID + 表名 + 主键)。如果全局锁被其他事务持有,则等待或重试,直到超时后回滚。

流程:先获取本地锁(数据库行锁),执行 SQL,提交本地事务,释放本地锁,然后获取全局锁(由 TC 管理)。只有获取全局锁成功后,一阶段才算完成。这避免了事务 A 提交后、事务 B 修改同一数据而事务 A 又需要回滚的冲突。

Q4: Seata 如何保证数据一致性?

Seata 通过以下机制保证数据一致性:

  1. 两阶段提交:一阶段执行 + 记录 UNDO_LOG,二阶段根据全局结果决定提交或回滚。
  2. UNDO_LOG:记录数据变更前后的快照,回滚时根据前镜像生成反向 SQL。
  3. 全局锁:防止并发事务间的脏写,保证写隔离。
  4. TC 协调:TC 维护全局事务状态,确保所有分支事务最终一致(全部提交或全部回滚)。
  5. 重试机制:TM 提交/回滚失败会重试(默认 5 次),RM 报告状态失败会重试。
  6. 定时任务:TC 对超时未完成的事务进行定时清理(定时回滚或提交)。

注意:AT 模式是最终一致性,不是强一致性。一阶段提交后到二阶段完成之间,其他事务可以读到已提交的数据(存在短暂的中间状态)。

Q5: Seata 的优缺点有哪些?

优点

  1. 零侵入:AT 模式仅需 @GlobalTransactional 注解,业务代码无需修改。
  2. 高性能:一阶段直接提交,不长时间锁定资源,性能远优于传统 2PC。
  3. 多模式支持:AT、TCC、Saga、XA 四种模式覆盖不同场景。
  4. 生态完善:支持 Dubbo、Spring Cloud、gRPC 等主流框架。
  5. 高可用:TC 支持集群部署,存储支持 db/redis 模式。

缺点

  1. 最终一致性:AT 模式不保证强一致性,有短暂中间状态,不适合强一致性要求场景。
  2. UNDO_LOG 膨胀:每个业务数据库都需要 UNDO_LOG 表,大数据量时可能膨胀。
  3. 全局锁开销:高并发下全局锁可能成为瓶颈。
  4. SQL 限制:AT 模式不支持跨数据库 JOIN、不支持存储过程等复杂 SQL。
  5. 运维成本:需要独立部署 TC Server,增加了运维复杂度。

Q6: 如果 Seata Server(TC)挂了怎么办?

  1. 高可用部署:TC 应部署为集群,通过 Nacos/Eureka 实现服务发现和负载均衡。
  2. 存储外置:使用 db 或 redis 模式存储事务会话,节点故障时其他节点可接管。
  3. 客户端容错:客户端 RM 本地事务已提交,数据已持久化;TC 恢复后可继续处理二阶段。
  4. 定时任务恢复:TC 恢复后,定时任务会扫描未完成的事务,重新驱动提交或回滚。
  5. 超时机制:如果 TM 长时间无法连接 TC,全局事务会超时,各 RM 可根据本地策略处理。

附录

A. 关键文件清单

文件路径说明
registry.confsrc/main/resources/registry.confSeata 注册中心配置
file.confseata-server/conf/file.confSeata Server 存储配置
application.ymlsrc/main/resources/application.yml业务服务 Seata 配置
undo_log 表各业务数据库AT 模式回滚日志表
global_table / branch_table / lock_tableSeata Server 数据库TC 事务会话表

B. 相关链接