Spring Boot微服务分布式事务处理:Seata AT模式集成与高并发场景实战

微服务架构下,跨服务的数据一致性是后端开发面临的核心挑战。Seata的AT模式通过两阶段提交和SQL解析实现自动化分布式事务,对业务代码侵入性低。本文记录Spring Boot集成Seata的完整流程。

Seata AT模式工作原理

AT模式分为两个阶段。第一阶段:拦截业务SQL,解析SQL语义找到操作的行记录,在事务提交前保存数据快照(before image)和操作后的数据快照(after image)到undo_log表,并在分支事务表中注册分支事务。第二阶段:如果全局事务决议提交,异步删除undo_log记录;如果决议回滚,根据before image生成反向SQL恢复数据。

核心角色说明:

  • TC(Transaction Coordinator):事务协调器,维护全局事务和分支事务状态
  • TM(Transaction Manager):事务管理器,定义全局事务范围,负责开启/提交/回滚
  • RM(Resource Manager):资源管理器,管理分支事务上的资源,向TC注册分支事务

Seata Server部署配置

使用Docker部署Seata Server,以Nacos作为注册中心和配置中心:

docker run -d --name seata-server \
  -p 8091:8091 \
  -p 7091:7091 \
  -e SEATA_IP=192.168.1.50 \
  -e SEATA_PORT=8091 \
  -e STORE_MODE=db \
  -e SEATA_CONFIG_NAME=file:/root/seata-config/registry \
  -v /data/seata/config:/root/seata-config \
  apache/seata-server:2.0.0

registry.conf配置文件:

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

config {
  type = "nacos"
  nacos {
    serverAddr = "192.168.1.40:8848"
    group = "SEATA_GROUP"
    namespace = ""
    dataId = "seataServer.properties"
  }
}

Spring Boot微服务集成Seata客户端

添加Maven依赖:

<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: yunthe_tx_group
  service:
    vgroup-mapping:
      yunthe_tx_group: default
    grouplist:
      default: 192.168.1.50:8091
  registry:
    type: nacos
    nacos:
      server-addr: 192.168.1.40:8848
      group: SEATA_GROUP
      namespace: ""
  config:
    type: nacos
    nacos:
      server-addr: 192.168.1.40:8848
      group: SEATA_GROUP
      namespace: ""
  data-source-proxy-mode: AT

业务代码实现:订单创建分布式事务

以电商下单场景为例,涉及订单服务、库存服务和账户服务三个微服务:

@Service
public class OrderService {

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

    @GlobalTransactional(name = "createOrder", 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 storageResult = storageFeignClient.decreaseStorage(
            dto.getProductId(), dto.getCount()
        );
        if (!storageResult.isSuccess()) {
            throw new RuntimeException("库存扣减失败: " + storageResult.getMessage());
        }

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

        // 4. 修改订单状态为已完成
        order.setStatus(1);
        orderMapper.updateById(order);

        return order;
    }
}

@GlobalTransactional注解标记全局事务入口。TM向TC发起全局事务开启请求,获取XID。XID通过RPC请求头传递到下游服务,RM自动注册分支事务。任意环节抛出异常,TC协调所有分支事务回滚。

高并发场景下的优化策略

高并发场景下Seata AT模式存在性能瓶颈,主要来自全局锁竞争和undo_log写入开销。优化方案如下:

方案一:热点数据使用Redis预扣减,减少数据库锁持有时间。

@Service
public class StorageService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private StorageMapper storageMapper;

    @Transactional(rollbackFor = Exception.class)
    public void decreaseStorage(Long productId, Integer count) {
        String key = "stock:" + productId;
        Long remaining = redisTemplate.opsForValue().increment(key, -count.longValue());

        if (remaining == null || remaining < 0) {
            redisTemplate.opsForValue().increment(key, count.longValue());
            throw new RuntimeException("库存不足");
        }

        // 异步更新数据库
        storageMapper.decreaseStorage(productId, count);
    }
}

方案二:将同步扣减改为消息中间件异步处理,降低事务持有时间。订单创建成功后发送MQ消息,库存和账户服务消费消息异步扣减。服务治理方面需配合幂等性检查,防止消息重复消费。

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 '状态',
    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模式回滚日志表';

Seata自动代理数据源,拦截SQL执行生成undo_log。如果使用多数据源,需要手动配置DataSourceProxy。排查分布式事务问题时,检查TC控制台的事务日志,确认各分支事务状态是否一致。XID日志追踪可以在日志框架中添加MDC过滤器,将XID打印到每条日志中便于关联。

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

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

相关推荐