Spring Boot分布式事务实战:Seata AT模式集成配置与生产调优指南

微服务架构下,跨服务的数据一致性是后端开发面临的核心挑战。Seata作为阿里开源的分布式事务中间件,其AT(Automatic Transaction)模式通过SQL解析和undo_log自动补偿机制,对业务代码侵入性极低。本文以Spring Boot项目为例,完整演示Seata Server部署、AT模式集成、事务注解使用及生产环境调优全过程。

Seata AT模式架构原理与组件职责

Seata AT模式包含三个核心角色:

  • TC(Transaction Coordinator):事务协调器,独立部署的Seata Server,负责维护全局事务和分支事务的状态,驱动全局提交或回滚
  • TM(Transaction Manager):事务管理器,定义全局事务的开启、提交、回滚边界,通过@GlobalTransactional注解标注
  • RM(Resource Manager):资源管理器,管理本地资源(数据库连接),注册分支事务,执行本地SQL并记录undo_log

AT模式的两阶段提交流程:

  • 一阶段:拦截业务SQL,解析SQL语义生成before image和after image,将前后镜像存入undo_log表。本地事务在同一个数据库事务中提交(业务SQL + undo_log写入),立即释放本地锁和连接资源
  • 二阶段-提交:TC通知各RM异步删除undo_log记录,因一阶段数据已落库,此操作非常快
  • 二阶段-回滚:TC通知各RM根据undo_log的before image反向生成补偿SQL,恢复数据到事务开始前状态

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

Seata Server使用Nacos作为注册中心和配置中心:

# docker-compose.yml 部署Seata Server 2.0
version: '3.8'
services:
  seata-server:
    image: seataio/seata-server:2.0.0
    container_name: seata-server
    ports:
      - "8091:8091"
      - "7091:7091"
    environment:
      - SEATA_PORT=8091
      - STORE_MODE=db
      - SEATA_IP=192.168.1.100
    volumes:
      - ./seata-config/application.yml:/seata-server/resources/application.yml

# application.yml
seata:
  config:
    type: nacos
    nacos:
      server-addr: 192.168.1.50:8848
      group: SEATA_GROUP
      namespace: seata
      data-id: seataServer.properties
  registry:
    type: nacos
    nacos:
      server-addr: 192.168.1.50:8848
      group: SEATA_GROUP
      namespace: seata
      cluster: default
  store:
    mode: db
    db:
      datasource: druid
      db-type: mysql
      url: jdbc:mysql://192.168.1.60:3306/seata?useUnicode=true
      user: seata
      password: SeataPass2026
      min-conn: 10
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      query-limit: 1000

Seata Server元数据库初始化SQL:

-- 创建seata数据库
CREATE DATABASE seata DEFAULT CHARACTER SET utf8mb4;

-- global_table: 全局事务表
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)
);

-- branch_table: 分支事务表
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,
  gmt_modified DATETIME,
  PRIMARY KEY (branch_id)
);

-- lock_table: 全局锁表
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(64),
  pk VARCHAR(36),
  status TINYINT,
  gmt_create DATETIME,
  gmt_modified DATETIME,
  PRIMARY KEY (row_key)
);

Spring Boot微服务集成Seata AT模式

Maven依赖引入:

<!-- pom.xml -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <version>2023.0.1.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: yunthe_tx_group
  service:
    vgroup-mapping:
      yunthe_tx_group: default
    grouplist:
      default: 192.168.1.100:8091
  registry:
    type: nacos
    nacos:
      server-addr: 192.168.1.50:8848
      group: SEATA_GROUP
      namespace: seata
  config:
    type: nacos
    nacos:
      server-addr: 192.168.1.50:8848
      group: SEATA_GROUP
      namespace: seata
  data-source-proxy-mode: AT

每个参与分布式事务的数据库必须创建undo_log表:

-- 在业务数据库中执行(order_db, inventory_db, account_db)
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 '状态',
  log_created DATETIME NOT NULL COMMENT '创建时间',
  log_modified DATETIME NOT NULL COMMENT '修改时间',
  ext VARCHAR(100) COMMENT '扩展信息',
  PRIMARY KEY (branch_id),
  UNIQUE KEY ux_undo_log (xid, branch_id)
) ENGINE=InnoDB COMMENT='AT模式undo_log表';

分布式事务注解使用与业务场景实现

以电商下单场景为例:订单服务创建订单 → 库存服务扣减库存 → 账户服务扣减余额。任一步骤失败则全部回滚。

// OrderService.java - 全局事务发起方
@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private InventoryFeignClient inventoryClient;
    @Autowired
    private AccountFeignClient accountClient;

    @GlobalTransactional(
        name = "create-order-tx",
        timeoutMills = 60000,
        rollbackFor = Exception.class
    )
    public Order createOrder(OrderDTO dto) {
        // 1. 创建订单
        Order order = new Order();
        order.setUserId(dto.getUserId());
        order.setProductId(dto.getProductId());
        order.setCount(dto.getCount());
        order.setMoney(dto.getMoney());
        order.setStatus(0); // 待支付
        orderMapper.insert(order);

        // 2. 远程调用库存服务扣减库存
        Result inventoryResult = inventoryClient.deduct(
            dto.getProductId(), dto.getCount()
        );
        if (!inventoryResult.isSuccess()) {
            throw new BusinessException("库存扣减失败: " + inventoryResult.getMessage());
        }

        // 3. 远程调用账户服务扣减余额
        Result accountResult = accountClient.debit(
            dto.getUserId(), dto.getMoney()
        );
        if (!accountResult.isSuccess()) {
            throw new BusinessException("余额扣减失败: " + accountResult.getMessage());
        }

        // 4. 更新订单状态
        order.setStatus(1); // 已支付
        orderMapper.updateStatus(order.getId(), 1);

        return order;
    }
}

// InventoryService.java - 分支事务参与方
@Service
public class InventoryService {

    @Autowired
    private InventoryMapper inventoryMapper;

    public boolean deduct(Long productId, Integer count) {
        Inventory inventory = inventoryMapper.selectByProductId(productId);
        if (inventory.getCount() < count) {
            throw new BusinessException("库存不足");
        }
        // Seata自动拦截此SQL,生成undo_log
        int rows = inventoryMapper.deductCount(productId, count);
        return rows > 0;
    }
}

Seata AT模式生产环境调优与常见排障

事务超时配置需根据业务实际执行时间调整。默认超时60秒,如果涉及多个远程调用且网络延迟较高,建议设为120秒。超时后TC会自动发起回滚,但已提交的分支事务需等待undo_log补偿。

# Nacos中seataServer.properties调优参数
# 全局事务重试间隔(毫秒)
client.rm.reportRetryCount = 5
client.rm.tableMetaCheckEnable = true
client.rm.tableMetaCheckerInterval = 60000
client.rm.sqlParserType = druid

# 异步提交时清理undo_log的线程池
client.rm.asyncCommitBufferLimit = 10000
client.rm.lock.retryInterval = 10
client.rm.lock.retryTimes = 30

# TM侧配置
client.tm.commitRetryCount = 3
client.tm.rollbackRetryCount = 3
client.tm.defaultGlobalTransactionTimeout = 60000

# undo_log序列化方式(jackson兼容性更好)
client.undo.logSerialization = jackson
client.undo.onlyCareUpdateColumns = true

常见排障场景:

场景1:分支事务注册失败。日志出现”can not connect to services-server”错误。排查Nacos注册中心网络连通性,确认tx-service-group与vgroup-mapping配置一致,Seata Server已注册到对应namespace。

场景2:undo_log序列化异常。日志出现”branch update error”。检查业务表是否包含主键,AT模式依赖主键生成前后镜像。如果表无主键需添加自增ID字段。JSON类型字段需确保序列化器支持(推荐jackson)。

场景3:全局锁竞争。高并发下出现”LockConflictException”。AT模式通过全局锁保证写隔离,同一行数据不能被两个全局事务同时修改。优化方案:缩短事务范围,减少锁持有时间;对非核心操作拆分到事务外执行;必要时改用Saga模式。

生产环境监控通过Seata内置的Metrics模块对接Prometheus:

# seata Server application.yml
seata:
  metrics:
    enabled: true
    registry-type: compact
    exporter-list: prometheus
    exporter-prometheus-port: 9898

# Prometheus采集配置
- job_name: 'seata'
  static_configs:
    - targets: ['192.168.1.100:9898']

关键监控指标包括:活跃全局事务数(seata.transaction.active)、分支事务数(seata.branch.active)、回滚率(seata.transaction.rollback.rate)、全局锁等待时间(seata.lock.wait.avg)。回滚率超过5%时需排查业务逻辑或数据竞争问题。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-fen-bu-shi-shi-wu-shi-zhan-seataat-mo-shi-ji/

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

相关推荐