Seata分布式事务AT模式原理剖析与实战部署

Seata是阿里巴巴开源的分布式事务解决方案,提供AT、TCC、SAGA和XA四种事务模式。AT模式凭借低侵入性和自动补偿机制,成为微服务架构中最常用的分布式事务方案。本文剖析AT模式的两阶段提交机制,并给出Spring Boot集成Seata的完整部署方案。

Seata AT模式两阶段提交机制

Seata架构包含三个核心角色:Transaction Coordinator(TC,事务协调器)、Transaction Manager(TM,事务管理器)和Resource Manager(RM,资源管理器)。TC独立部署,TM和RM以SDK形式集成在应用中。

AT模式的两个阶段:

阶段一(Phase 1):各RM执行本地业务SQL并提交本地事务。在提交前,RM拦截SQL解析前后镜像数据,生成undo log并写入undo_log表。本地事务提交后,RM向TC注册分支事务并上报执行结果。

阶段二(Phase 2):TM根据所有分支事务的执行结果向TC发送全局提交或回滚指令。如果全部成功,TC通知各RM异步删除undo log;如果有任一分支失败,TC通知各RM根据undo log反向补偿数据。

-- undo_log表结构
CREATE TABLE undo_log (
    branch_id     BIGINT       NOT NULL COMMENT '分支事务ID',
    xid           VARCHAR(128) NOT NULL COMMENT '全局事务ID',
    context       VARCHAR(128) NOT NULL COMMENT '上下文',
    rollback_info LONGBLOB     NOT NULL COMMENT '回滚数据(前后镜像)',
    log_status    INT          NOT NULL COMMENT '状态:0正常,1全局已完成',
    log_created   DATETIME     NOT NULL COMMENT '创建时间',
    log_modified  DATETIME     NOT NULL COMMENT '修改时间',
    UNIQUE KEY ux_undo_log (xid, branch_id)
) ENGINE=InnoDB;

undo log存储的是变更前后的数据快照。回滚时,Seata根据before image恢复数据,并校验after image与当前数据库中的值是否一致。如果不一致,说明数据在事务期间被其他操作修改,Seata会抛出BranchRollbackFailed_Retriable异常,记录脏写并人工介入。

Seata Server部署与Nacos注册中心配置

TC独立部署,以下为基于Docker的Seata Server 2.0部署方案,使用Nacos作为注册和配置中心:

# docker-compose.yml
version: '3'
services:
  seata-server:
    image: seataio/seata-server:2.0.0
    ports:
      - "8091:8091"
      - "7091:7091"
    environment:
      - SEATA_PORT=8091
      - STORE_MODE=db
      - SEATA_CONFIG_NAME=file:/root/seata-config/registry
    volumes:
      - ./seata-config:/root/seata-config
      - ./application.yml:/seata-server/resources/application.yml

application.yml核心配置:

seata:
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: seata
      data-id: seataServer.properties
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: seata
  store:
    mode: db
    db:
      datasource: druid
      db-type: mysql
      url: jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true
      user: root
      password: ${DB_PASSWORD}
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table

Seata的三个核心存储表(global_table、branch_table、lock_table)存储全局事务和分支事务的元数据。lock_table实现全局锁,保证同一行数据在同一全局事务内不会被并发修改。

Spring Boot微服务集成Seata AT模式

以订单服务(创建订单)和库存服务(扣减库存)的分布式事务为例:

<!-- pom.xml -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-client</artifactId>
    <version>2.3.0</version>
</dependency>
# application.yml
seata:
  enabled: true
  application-id: order-service
  tx-service-group: default_tx_group
  service:
    vgroup-mapping:
      default_tx_group: default
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: seata
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: seata
  data-source-proxy-mode: AT

data-source-proxy-mode: AT启用AT模式,Seata会自动代理DataSource,拦截所有SQL操作生成undo log。业务代码只需在入口方法添加@GlobalTransactional注解:

@Service
public class OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @Autowired
    private InventoryFeignClient inventoryClient;
    
    @GlobalTransactional(timeoutMills = 60000, name = "createOrder")
    public void createOrder(OrderDTO dto) {
        // 1. 创建订单(本地事务,RM自动注册分支事务)
        Order order = new Order();
        order.setProductId(dto.getProductId());
        order.setQuantity(dto.getQuantity());
        order.setUserId(dto.getUserId());
        order.setStatus("CREATED");
        orderMapper.insert(order);
        
        // 2. 远程调用库存服务扣减库存(分支事务)
        Result<Void> result = inventoryClient.deduct(
            dto.getProductId(), dto.getQuantity()
        );
        
        if (!result.isSuccess()) {
            throw new RuntimeException("库存扣减失败: " + result.getMessage());
        }
        
        // 3. 远程调用账户服务扣款(分支事务)
        Result<Void> payResult = accountClient.debit(
            dto.getUserId(), dto.getAmount()
        );
        
        if (!payResult.isSuccess()) {
            throw new RuntimeException("扣款失败");
        }
    }
}

@GlobalTransactional标记的方法内,所有数据库操作和远程调用都纳入同一个全局事务。任一步骤抛出异常,TC会通知所有已注册的RM执行回滚补偿。库存服务的扣减操作不需要任何事务注解,Seata通过DataSource代理自动参与全局事务。

全局锁与并发冲突处理

AT模式通过全局锁实现写隔离。阶段一本地事务提交前,RM会向TC申请获取涉及数据行的全局锁。如果该行已被其他全局事务锁定,当前事务会等待最多10秒(可配置),超时后抛出LockConflictException。

# 锁超时配置
seata:
  client:
    rm:
      lock:
        retry-interval: 30
        retry-times: 10
        retry-policy-branch-rollback-on-conflict: true

读隔离方面,AT模式默认使用读已提交级别。阶段一本地事务提交后、全局事务尚未完成时,其他事务可以读到未提交的脏数据。如果需要读已提交的全局隔离级别,需要在查询SQL前使用@GlobalLock + SELECT FOR UPDATE:

@Service
public class InventoryService {
    
    @GlobalLock
    @Transactional
    public Inventory getInventoryForUpdate(Long productId) {
        // SELECT ... FOR UPDATE 会触发全局锁检查
        return inventoryMapper.selectForUpdate(productId);
    }
}

生产环境部署注意事项

undo_log清理:全局事务完成后,undo log由RM异步删除。如果应用异常退出,可能残留undo log记录。定期执行清理任务,删除超过24小时的已完成事务记录。

TC高可用:部署至少3个TC实例,通过Nacos实现服务发现和负载均衡。TC使用DB模式存储事务状态,多个TC实例共享同一数据库,天然支持水平扩展。

事务超时配置:timeoutMills默认60秒。涉及多个远程调用的长链路事务需适当增大超时时间,但不宜超过120秒,避免长时间持有全局锁导致性能问题。

幂等性设计:网络抖动可能导致TC的重试请求到达RM。库存扣减等操作需保证幂等性,通过唯一请求ID或状态机防止重复执行。

AT模式通过自动生成undo log实现补偿回滚,业务代码侵入性低,适合大多数OLTP场景。但对于涉及外部系统调用(如第三方支付)的操作,AT模式无法自动补偿,需配合TCC模式或SAGA模式处理。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/seata-fen-bu-shi-shi-wu-at-mo-shi-yuan-li-pou-xi-yu-shi/

(0)
小编小编
上一篇 16小时前
下一篇 16小时前

相关推荐