Spring Boot微服务分布式事务解决方案:Seata AT模式生产实战

微服务架构下的事务困境

单体应用中,一个数据库事务就能保证数据一致性。微服务架构下,一个业务操作涉及多个服务、多个数据库,本地事务无法覆盖跨服务调用。库存扣减成功但订单创建失败,或者反过来——数据不一致在生产环境中是绝对不能接受的。

Seata是当前Java微服务生态中最成熟的分布式事务框架。AT模式(Auto Transaction)对业务代码侵入性最小,只需一个@GlobalTransactional注解,框架自动处理两阶段提交的回滚补偿。

Seata AT模式的工作原理

AT模式的核心逻辑分两阶段:

一阶段:拦截业务SQL,生成前镜像(before image)和后镜像(after image),写入undo_log表,然后执行业务SQL并提交本地事务。

二阶段:若全局事务提交,异步清理undo_log;若全局事务回滚,根据undo_log中的前镜像反向补偿(生成反向SQL),恢复数据到事务开始前的状态。

/* 一阶段执行流程(以UPDATE为例) */
-- 1. 查询前镜像
SELECT id, stock FROM product WHERE id = 1001;
-- before_image: {id: 1001, stock: 100}

-- 2. 执行业务SQL
UPDATE product SET stock = stock - 1 WHERE id = 1001;

-- 3. 查询后镜像
SELECT id, stock FROM product WHERE id = 1001;
-- after_image: {id: 1001, stock: 99}

-- 4. 写入undo_log
INSERT INTO undo_log (branch_id, xid, context, rollback_info)
VALUES (2001, 'tx-123456', 'AT',
  '{"beforeImage":{"stock":100},"afterImage":{"stock":99}}');

-- 5. 提交本地事务

两阶段回滚时,Seata读取undo_log的前镜像,生成反向SQL恢复数据。

Seata Server部署与配置

Seata Server作为事务协调者(TC),独立部署:

# docker-compose部署Seata Server
version: '3.8'
services:
  seata-server:
    image: seataio/seata-server:2.0.0
    container_name: seata-server
    ports:
      - "8091:8091"
      - "7091:7091"
    restart: unless-stopped

Seata Server配置(application.yml):

server:
  port: 7091

seata:
  config:
    type: nacos
    nacos:
      server-addr: 192.168.1.50:8848
      namespace: seata
      group: SEATA_GROUP
  registry:
    type: nacos
    nacos:
      server-addr: 192.168.1.50:8848
      namespace: seata
      group: SEATA_GROUP
  store:
    mode: db
    db:
      datasource: druid
      db-type: mysql
      url: jdbc:mysql://192.168.1.50:3306/seata?useSSL=false
      user: seata
      password: seata_pwd

微服务接入Seata客户端

<dependency>
  <groupId>io.seata</groupId>
  <artifactId>seata-spring-boot-starter</artifactId>
  <version>2.0.0</version>
</dependency>
seata:
  enabled: true
  application-id: order-service
  tx-service-group: my_test_tx_group
  service:
    vgroup-mapping:
      my_test_tx_group: default
  registry:
    type: nacos
    nacos:
      server-addr: 192.168.1.50:8848
      namespace: seata

每个参与方数据库创建undo_log表:

CREATE TABLE IF NOT EXISTS undo_log (
  branch_id     BIGINT       NOT NULL,
  xid           VARCHAR(128) NOT NULL,
  context       VARCHAR(128) NOT NULL,
  rollback_info LONGBLOB     NOT NULL,
  log_status    INT          NOT NULL,
  log_created   DATETIME     NOT NULL,
  log_expired   DATETIME     NOT NULL,
  PRIMARY KEY (branch_id),
  KEY idx_xid (xid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

业务代码:@GlobalTransactional注解实战

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private StockFeignClient stockClient;

    @Autowired
    private AccountFeignClient accountClient;

    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public Order createOrder(OrderDTO orderDTO) {
        Order order = new Order();
        order.setUserId(orderDTO.getUserId());
        order.setProductId(orderDTO.getProductId());
        order.setAmount(orderDTO.getAmount());
        order.setStatus("CREATED");
        orderMapper.insert(order);

        stockClient.deduct(orderDTO.getProductId(), orderDTO.getQuantity());
        accountClient.debit(orderDTO.getUserId(), orderDTO.getAmount());

        return order;
    }
}

服务治理与高并发场景下的注意事项

Seata AT模式在正常场景下工作良好,但高并发写场景存在全局锁争用问题。全局锁保证跨服务数据的一致性,但代价是降低并发吞吐量。

seata:
  client:
    rm:
      lock:
        retry-interval: 10
        retry-times: 30
        retry-policy-branch-rollback-on-conflict: true

API接口规范建议:分布式事务只包裹必要的服务调用,不要在@GlobalTransactional内执行耗时操作(如文件上传、消息发送),这些会延长全局锁持有时间,加剧锁争用。对一致性要求不那么严格的场景,可用RocketMQ事务消息替代Seata。事务消息的吞吐量远高于Seata AT模式,但编程复杂度更高,需要实现本地事务状态的回查接口。

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

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

相关推荐

Spring Boot微服务分布式事务解决方案:Seata AT模式实战部署

微服务场景下分布式事务的必要性

单体应用中数据库本地事务能保证ACID,微服务拆分后,一次业务操作横跨多个服务的数据库,本地事务无法保证跨库一致性。典型场景:电商下单需要同时操作订单库、库存库、账户库,三个操作必须全部成功或全部回滚。Seata的AT模式对业务代码侵入最小,只需加一个@GlobalTransactional注解。

Seata AT模式原理

AT模式分两阶段:一阶段拦截SQL,生成前镜像和后镜像写入undo_log表,然后执行业务SQL并提交本地事务;二阶段由TC协调,全部成功则异步清理undo_log,有失败则根据undo_log反向补偿。核心是拦截SQL加自动补偿,业务层只需加注解,不需要手动写补偿逻辑。

组件角色:

  • TC(Transaction Coordinator):事务协调者,维护全局事务状态
  • TM(Transaction Manager):事务管理器,开启/提交/回滚全局事务
  • RM(Resource Manager):资源管理器,管控分支事务

Seata Server部署

Seata Server作为TC独立部署,用Nacos做注册中心:

# docker-compose-seata.yml
version: '3'
services:
  seata-server:
    image: seataio/seata-server:2.0.0
    container_name: seata-server
    ports:
      - "8091:8091"
      - "7091:7091"
    environment:
      - SEATA_IP=192.168.1.100
    volumes:
      - ./seata-config:/seata-server/resources
    depends_on:
      - nacos

application.yml配置注册中心和存储模式:

seata:
  registry:
    type: nacos
    nacos:
      server-addr: 192.168.1.100:8848
      namespace: seata
      group: DEFAULT_GROUP
  store:
    mode: db
    db:
      datasource: druid
      db-type: mysql
      url: jdbc:mysql://192.168.1.100:3306/seata?rewriteBatchedStatements=true
      user: seata
      password: seata_pwd

微服务集成Seata客户端

每个参与分布式事务的微服务需要添加Seata依赖和配置:

<!-- pom.xml -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <version>2022.0.0.0</version>
</dependency>

application.yml中配置Seata事务组:

seata:
  tx-service-group: order-tx-group
  service:
    vgroup-mapping:
      order-tx-group: default
  registry:
    type: nacos
    nacos:
      server-addr: 192.168.1.100:8848
      namespace: seata

每个微服务数据库需创建undo_log表:

CREATE TABLE IF NOT EXISTS `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) 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 DEFAULT CHARSET=utf8;

业务代码:一个注解搞定分布式事务

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private StockClient stockClient;
    @Autowired
    private AccountClient accountClient;

    @Override
    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public void createOrder(OrderDTO orderDTO) {
        // 1. 创建订单
        Order order = new Order();
        order.setUserId(orderDTO.getUserId());
        order.setCommodityCode(orderDTO.getCommodityCode());
        order.setCount(orderDTO.getCount());
        order.setMoney(orderDTO.getMoney());
        orderMapper.insert(order);

        // 2. 扣减库存(远程调用库存服务)
        stockClient.deduct(orderDTO.getCommodityCode(), orderDTO.getCount());

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

如果库存扣减或账户扣款失败抛异常,Seata自动回滚已执行的分支事务,undo_log中保存的前镜像数据用于反向补偿。

生产环境注意事项

超时配置:全局事务默认超时60秒,长事务需调整seata.client.tm.global-transaction-timeout参数。

隔离级别:AT模式一阶段已提交本地事务,默认读未提交。如需全局读已提交,配置seata.client.undo.log-serialization=jackson并开启全局锁。

undo_log清理:二阶段完成后undo_log由Seata自动清理,但异常中断时可能残留,需定时任务清理超过24小时的记录。

性能优化:高并发场景下undo_log写入是瓶颈,确保undo_log表和业务表在同一数据库实例,减少网络开销。

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

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

相关推荐

Spring Boot微服务分布式事务解决方案:Seata AT模式实战配置

微服务场景下为什么需要分布式事务

单体应用拆成微服务后,原本一个本地事务就能搞定的业务,变成了跨服务的调用链。订单服务创建订单、库存服务扣减库存、账户服务扣减余额,任何一步失败都需要全部回滚。Spring的@Transactional管不了跨JVM的事务,这就是分布式事务要解决的问题。

分布式事务方案很多(2PC、TCC、Saga、可靠消息最终一致性),Seata的AT模式是对业务侵入最小的一种——只需加一个注解,框架自动生成回滚日志,不需要手动写补偿逻辑。

Seata Server部署

Seata AT模式需要一个协调者(TC, Transaction Coordinator)来管理全局事务。先部署Seata Server:

# 下载Seata Server 2.x
wget https://github.com/seata/seata/releases/download/2.2.0/seata-server-2.2.0.tar.gz
tar -xzf seata-server-2.2.0.tar.gz
cd seata-server-2.2.0

配置Seata Server使用Nacos做注册中心,MySQL做事务日志存储:

# conf/application.yml
server:
  port: 7091

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

初始化Seata数据库表(执行Seata提供的SQL脚本),然后启动:

sh bin/seata-server.sh

微服务接入Seata客户端

三个服务:order-service、stock-service、account-service,都接入Seata AT模式。

Maven依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <version>2023.0.1.0</version>
</dependency>

每个服务的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

每个服务的数据库都需要建undo_log表,Seata用它记录回滚数据:

CREATE TABLE IF NOT EXISTS undo_log (
    branch_id     BIGINT       NOT NULL,
    xid           VARCHAR(128) NOT NULL,
    context       VARCHAR(128) NOT NULL,
    rollback_info LONGBLOB     NOT NULL,
    log_status    INT          NOT NULL,
    log_created   DATETIME     NOT NULL,
    log_modified  DATETIME     NOT NULL,
    PRIMARY KEY (branch_id),
    KEY idx_xid (xid)
) ENGINE=InnoDB;

全局事务注解使用

在订单服务的业务入口添加@GlobalTransactional

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private StockClient stockClient;
    @Autowired
    private AccountClient accountClient;

    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public Order createOrder(OrderDTO dto) {
        // 1. 创建订单
        Order order = new Order();
        order.setUserId(dto.getUserId());
        order.setCommodityCode(dto.getCommodityCode());
        order.setCount(dto.getCount());
        order.setMoney(dto.getMoney());
        order.setStatus("CREATING");
        orderMapper.insert(order);

        // 2. 扣减库存(远程调用)
        stockClient.deduct(dto.getCommodityCode(), dto.getCount());

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

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

        return order;
    }
}

如果第三步账户余额不足抛异常,Seata会自动回滚第一步和第二步的数据库操作。原理是Seata拦截了SQL执行,在执行前把原始数据快照写入undo_log,回滚时从undo_log恢复。

库存服务与账户服务实现

被调用方不需要加@GlobalTransactional,Seata通过XID传播自动加入全局事务:

@Service
public class StockService {

    @Autowired
    private StockMapper stockMapper;

    public void deduct(String commodityCode, int count) {
        Stock stock = stockMapper.selectByCode(commodityCode);
        if (stock == null || stock.getCount() < count) {
            throw new RuntimeException("库存不足");
        }
        stock.setCount(stock.getCount() - count);
        stockMapper.updateById(stock);
    }
}

Seata通过拦截MyBatis/JPA的SQL执行,自动将操作纳入分布式事务管理。XID通过Dubbo或Feign的拦截器在服务间传播。

事务超时与重试配置

分布式事务超时默认60秒,业务长的场景需要调大:

@GlobalTransactional(
    name = "create-order",
    timeoutMills = 300000,  // 5分钟
    rollbackFor = Exception.class
)

全局事务重试和超时配置:

seata:
  client:
    rm:
      async-commit-buffer-limit: 10000
      report-retry-count: 5
      table-meta-check-enable: true
    tm:
      commit-retry-count: 3
      rollback-retry-count: 3
      default-global-transaction-timeout: 60000

生产环境注意事项

1. undo_log清理:Seata默认不清理undo_log,需要定时任务清理已完成事务的记录:

-- 每天清理超过7天的undo_log
DELETE FROM undo_log WHERE log_created < DATE_SUB(NOW(), INTERVAL 7 DAY);

2. 全局锁冲突:AT模式通过全局锁保证隔离性,写冲突时后到的请求会等待或失败。高并发场景考虑用TCC模式替代。

3. Seata Server高可用:生产环境至少部署2个Seata Server实例,注册到Nacos实现高可用。Seata Server本身无状态,事务日志存在数据库里,随便扩容。

4. 异常兜底:Seata的回滚不是100%成功——undo_log被手动删除、数据库DDL变更都会导致回滚失败。需要监控global_table中status长期为BeginTimeout或RollbackRetrying的事务,人工介入处理。

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

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

相关推荐