Spring Boot集成Seata AT模式:分布式事务实战配置与问题排查

微服务拆分后的事务难题

单体应用中一个本地事务就能保证数据一致性,微服务架构下订单创建涉及订单服务、库存服务、账户服务三个独立数据库,本地事务无法覆盖跨服务的数据操作。库存扣减成功但账户扣款失败时,库存不会自动回滚,造成数据不一致。

Seata的AT模式通过拦截SQL、自动生成回滚日志,在业务层实现分布式事务的自动补偿。与TCC模式相比,AT模式对业务代码侵入最小——只需加一个@GlobalTransactional注解。

Seata Server部署

Seata AT模式需要TC(Transaction Coordinator)协调器独立运行。

Docker部署Seata Server

# 拉取镜像
docker pull seataio/seata-server:2.0.0

# 创建配置目录
mkdir -p /opt/seata/config

# 编写application.yml
cat > /opt/seata/config/application.yml <<'EOF'
server:
  port: 7091

seata:
  config:
    type: nacos
    nacos:
      server-addr: nacos:8848
      namespace: seata
      group: SEATA_GROUP
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: nacos:8848
      namespace: seata
      group: SEATA_GROUP
  store:
    mode: db
    datasource: druid
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://mysql:3306/seata?rewriteBatchedStatements=true
      user: seata
      password: seata_pwd
      min-conn: 10
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      distributed-lock-table: distributed_lock
EOF

# 启动Seata Server
docker run -d --name seata-server \
  -p 8091:8091 -p 7091:7091 \
  -v /opt/seata/config:/seata-server/resources \
  seataio/seata-server:2.0.0

创建Seata元数据表

在Seata专用数据库中执行建表SQL:

-- global_table: 存储全局事务状态
CREATE TABLE IF NOT EXISTS `global_table`
(
  `xid`                       VARCHAR(128) NOT NULL,
  `transaction_name`          VARCHAR(128),
  `status`                    TINYINT      NOT NULL,
  `application_id`            VARCHAR(32),
  `transaction_service_group` VARCHAR(32),
  `begin_time`                BIGINT,
  PRIMARY KEY (`xid`)
) ENGINE = InnoDB;

-- branch_table: 存储分支事务状态
CREATE TABLE IF NOT EXISTS `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),
  PRIMARY KEY (`branch_id`)
) ENGINE = InnoDB;

-- lock_table: 存储全局锁
CREATE TABLE IF NOT EXISTS `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),
  PRIMARY KEY (`row_key`)
) ENGINE = InnoDB;

业务数据库Undo Log表

AT模式的核心是undo_log——Seata在执行业务SQL前自动生成回滚快照,异常时根据快照逆补偿。每个参与分布式事务的微服务数据库都需要建这张表:

-- 每个业务库都要建
CREATE TABLE IF NOT EXISTS `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(11)      NOT NULL COMMENT '日志状态',
  `log_created`   DATETIME(6)  NOT NULL COMMENT '创建时间',
  `log_modified`  DATETIME(6)  NOT NULL COMMENT '修改时间',
  UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB COMMENT ='AT transaction mode undo table';

Spring Boot微服务集成Seata

以订单服务为例,展示完整的集成流程。

Maven依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <version>2023.0.1.0</version>
</dependency>
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>2.0.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
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: seata
      group: SEATA_GROUP

业务代码中的事务注解

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private StockClient stockClient;

    @Autowired
    private AccountClient accountClient;

    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public OrderDTO createOrder(OrderRequest request) {
        // 1. 创建订单
        Order order = new Order();
        order.setUserId(request.getUserId());
        order.setProductId(request.getProductId());
        order.setQuantity(request.getQuantity());
        order.setAmount(request.getAmount());
        order.setStatus("CREATING");
        orderMapper.insert(order);

        // 2. 扣减库存(远程调用库存服务)
        stockClient.deduct(request.getProductId(), request.getQuantity());

        // 3. 扣减账户余额(远程调用账户服务)
        accountClient.debit(request.getUserId(), request.getAmount());

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

        return OrderDTO.fromEntity(order);
    }
}

只需一个@GlobalTransactional注解,Seata自动协调三个服务的本地事务。任一步骤抛出异常,所有已执行的分支事务自动回滚。

数据源代理配置

AT模式通过拦截DataSource的SQL执行来生成undo_log。Spring Boot自动注入的数据源需要被Seata代理:

@Configuration
public class SeataDataSourceConfig {

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

    @Bean
    @Primary
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }
}

Spring Cloud Alibaba Seata Starter 2023版本已自动完成代理,无需手动配置。如果日志中出现”dataSourceProxy not found”,需要确认Starter版本和自动配置是否生效。

分布式事务排障实战

全局事务超时回滚

Seata默认全局超时60秒。库存服务处理慢导致超时,全局事务被强制回滚:

# 解决方案:调整超时时间
@GlobalTransactional(timeoutMills = 300000)  # 5分钟

全局锁冲突

两个全局事务同时操作同一行数据,后到的事务获取全局锁失败:

# 日志特征
# Could not acquire global lock on table:stock, pk:1001

# 方案1:设置重试次数和间隔
seata:
  client:
    lock:
      retry-times: 30
      retry-interval: 1000

# 方案2:业务层做乐观锁,避免长时间持有全局锁

脏回滚问题

undo_log中保存的是SQL执行前的数据快照。如果在全局事务执行期间,非Seata管理的程序直接修改了数据库,回滚时会覆盖这些修改,造成数据丢失。避免方案:所有数据修改必须经过Seata代理的数据源。

Seata AT模式在业务侵入性和可靠性之间取得了平衡。通过SQL拦截自动生成回滚日志,开发者只需一个注解就能获得分布式事务保障,代价是undo_log的存储开销和全局锁的性能影响。在高并发写场景下需要评估全局锁对吞吐量的影响,必要时切换为TCC模式。

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

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

相关推荐