Spring Boot 3.x分布式事务Seata集成与生产排错指南

微服务架构下分布式事务的必要性

单体应用拆分为微服务后,本地事务无法覆盖跨服务的数据一致性需求。订单服务创建订单、库存服务扣减库存、账户服务扣减余额,三个操作分布在三个独立数据库中,任何一步失败都需要全部回滚。Seata作为目前最活跃的开源分布式事务框架,提供了AT、TCC、Saga、XA四种模式,其中AT模式对业务代码侵入最小,是Spring Boot项目的首选方案。

Seata AT模式原理与架构

AT模式的核心是”两阶段提交+全局锁”。一阶段:拦截业务SQL,生成前镜像和后镜像,写入undo_log表,提交本地事务并释放本地锁。二阶段:全局提交时异步清理undo_log;全局回滚时根据undo_log反向补偿数据。

Seata Server(TC)作为事务协调器独立部署,TM(事务管理器)和RM(资源管理器)集成在业务应用中。生产环境TC需集群部署,使用Nacos或Redis作为注册中心。

Spring Boot 3.x集成Seata完整步骤

1. 引入依赖

<!-- pom.xml -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.8.0</version>
</dependency>

<!-- 数据源代理 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-seata</artifactId>
    <version>2023.0.1.0</version>
</dependency>

2. 配置Seata客户端

# application.yml
seata:
  enabled: true
  application-id: order-service
  tx-service-group: my-tx-group
  service:
    vgroup-mapping:
      my-tx-group: default
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: seata
      group: SEATA_GROUP
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: seata
      group: SEATA_GROUP

3. 每个参与事务的数据库创建undo_log表

-- undo_log表(每个业务库都要创建)
CREATE TABLE IF NOT EXISTS undo_log (
    id            BIGINT       NOT NULL AUTO_INCREMENT,
    branch_id     BIGINT       NOT NULL,
    xid           VARCHAR(128) NOT NULL,
    context       MEDIUMBLOB   NOT NULL,
    rollback_info MEDIUMBLOB   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)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

4. 业务代码使用@GlobalTransactional

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private InventoryClient inventoryClient;

    @Autowired
    private AccountClient accountClient;

    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public Order createOrder(OrderDTO dto) {
        // 1. 创建订单(本地事务,Seata自动代理)
        Order order = new Order();
        order.setUserId(dto.getUserId());
        order.setProductId(dto.getProductId());
        order.setAmount(dto.getAmount());
        order.setStatus("INIT");
        orderMapper.insert(order);

        // 2. 扣减库存(远程调用)
        inventoryClient.deduct(dto.getProductId(), dto.getQuantity());

        // 3. 扣减余额(远程调用)
        accountClient.debit(dto.getUserId(), dto.getAmount());

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

        return order;
    }
}

@GlobalTransactional标注的方法内,所有数据库操作和Feign远程调用自动纳入分布式事务。任何一步抛出异常,Seata TC通知所有参与分支回滚。

数据源代理配置避坑

Seata AT模式必须代理数据源才能拦截SQL生成undo_log。Spring Boot 3.x使用HikariCP时,常见错误是代理顺序不对:

@Configuration
public class DataSourceProxyConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    public DataSource dataSource(DataSourceProperties properties) {
        // 先创建原始数据源
        HikariDataSource hikariDataSource = properties
            .initializeDataSourceBuilder()
            .type(HikariDataSource.class)
            .build();

        // Seata代理包装
        return new DataSourceProxy(hikariDataSource);
    }
}

注意:不要在Seata代理外再套一层Druid代理,双层代理会导致SQL解析失败。如果需要Druid监控,建议在Seata代理的内层使用DruidDataSource。

生产环境高频故障排查

1. 全局锁等待超时

两个分布式事务操作同一行数据时,后到的事务等待全局锁直到超时。默认超时30秒:

# 调整全局锁超时(Seata配置中心)
client.rm.lock.retry-times=10
client.rm.lock.retry-interval=50

2. undo_log未清理导致表膨胀

正常情况下Seata自动清理undo_log。但TC宕机时可能出现残留记录。定期清理超过24小时的记录:

-- 定时清理脚本
DELETE FROM undo_log
WHERE log_created < DATE_SUB(NOW(), INTERVAL 24 HOUR)
  AND log_status = 1;

3. 全局事务悬挂

分支事务回滚通知先于注册通知到达时,全局事务处于悬挂状态。开启悬挂防护:

# Seata配置
client.rm.async-commit-buffer-limit=10000
client.rm.report-retry-count=5
client.rm.table-meta-check-enable=true

4. XID未传递到下游服务

Feign调用时XID通过HTTP Header传递。确认拦截器已注册:

@Bean
public RequestInterceptor seataFeignInterceptor() {
    return template -> {
        String xid = RootContext.getXID();
        if (xid != null) {
            template.header(RootContext.KEY_XID, xid);
        }
    };
}

Seata分布式事务的本质是用性能换一致性。AT模式通过SQL解析和自动补偿降低了开发成本,但全局锁机制在高并发写场景下有性能瓶颈。写入冲突频繁的业务(如秒杀库存扣减),优先考虑TCC模式或业务层幂等设计,AT模式适合读多写少、冲突概率低的常规业务流程。

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

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

相关推荐