当前位置: 首页 > news >正文

Seata框架 分布式事务实战 Demo-1

以下是一个基于 Seata框架 的分布式事务实战 Demo,使用 AT模式(Automatic Transaction) 实现类似 3PC 的效果。AT 模式是 Seata 的核心模式,结合了 2PC 和乐观锁,通过自动记录 Undo Log 实现高效回滚。


环境准备

  1. 下载 Seata Server
    访问 Seata官网 下载最新版(以 2.1.0 为例),解压后启动:

    # 启动 TC(Transaction Coordinator)
    sh seata-server.bat -p 8091 -h 127.0.0.1
    
    # 启动 TM(Transaction Manager)和 RM(Resource Manager)
    sh seata-server.bat -p 8092 -h 127.0.0.1
    
  2. 配置数据库
    创建 MySQL 数据库并初始化 undo_log 表(Seata 回滚依赖):

    CREATE DATABASE seata_order DEFAULT CHARSET=utf8mb4;
    USE seata_order;
    
    CREATE TABLE `order` (
      `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
      `user_id` VARCHAR(50),
      `amount` DECIMAL(10,2),
      `status` ENUM('CREATED', 'COMMITTED', 'ROLLED_BACK') DEFAULT 'CREATED'
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    
    CREATE TABLE `inventory` (
      `sku` VARCHAR(50) PRIMARY KEY,
      `stock` INT DEFAULT 100
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    
    -- undo_log 表(Seata 自动创建)
    CREATE TABLE `undo_log` (
      `branch_transaction_id` BIGINT PRIMARY KEY,
      `xid` VARCHAR(100),
      `rollback_info` LONGBLOB,
      `log_table_name` VARCHAR(100),
      `log_table_column` VARCHAR(100),
      `log_key` VARCHAR(200),
      `timestamp` DATETIME DEFAULT CURRENT_TIMESTAMP
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    
  3. 注册中心与服务发现
    使用 Nacos 作为注册中心,启动 Nacos Server 并配置 Seata Server 的 application.yml

    # seata-server/config/application.yml
    spring:
      application:
        name: seata-server
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/seata_order?useSSL=false&serverTimezone=UTC
        username: root
        password: root
      registry:
        type: nacos
        nacos:
          server-addr: 127.0.0.1:8848
      config:
        center:
          type: nacos
          nacos:
            server-addr: 127.0.0.1:8848
    

Spring Boot 项目集成 Seata

1. 添加依赖

pom.xml 中添加 Seata 和 MySQL 驱动依赖:

<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.8</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
2. 配置数据源代理

application.yml 中配置 Seata 数据源代理:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/seata_order?useSSL=false&serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      max-active: 20
  seata:
    tx-service-group: my_test_tx_group
    enable-auto-commit: false
    service-component:
      tx-manager:
        service-name: seata-tm-service
        register-center:
          type: nacos
          nacos:
            server-addr: 127.0.0.1:8848
      tx-coordinator:
        service-name: seata-tc-service
        register-center:
          type: nacos
          nacos:
            server-addr: 127.0.0.1:8848
3. 启用全局事务注解

在启动类上添加 @EnableSeataTx 注解:

@SpringBootApplication
@EnableSeataTx
public class SeataOrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(SeataOrderApplication.class, args);
    }
}

业务代码实现

1. 订单服务(OrderService)
@Service
public class OrderService {

    @Autowired
    private OrderDAO orderDAO;

    @Autowired
    private InventoryService inventoryService;

    @Autowired
    private PaymentService paymentService;

    @GlobalTransactional(timeout=30000, name="createOrder", rollbackFor=Exception.class)
    public void createOrder(Order order) {
        // 1. 扣减库存
        inventoryService.deduct(order.getSkuId());
        
        // 2. 扣款(支付服务调用)
        paymentService.charge(order.getUserId(), order.getAmount());
        
        // 3. 生成订单
        orderDAO.insert(order);
    }
}
2. 库存服务(InventoryService)
@Service
public class InventoryService {

    @Autowired
    private InventoryDAO inventoryDAO;

    @Transactional
    public void deduct(String sku) {
        Inventory inventory = inventoryDAO.findBySku(sku);
        if (inventory.getStock() <= 0) {
            throw new RuntimeException("库存不足,SKU: " + sku);
        }
        inventory.setStock(inventory.getStock() - 1);
        inventoryDAO.update(inventory);
    }
}
3. 支付服务(PaymentService)
@Service
public class PaymentService {

    @Autowired
    private PaymentDAO paymentDAO;

    @Transactional
    public void charge(String userId, BigDecimal amount) {
        Payment payment = paymentDAO.findByUserId(userId);
        if (payment == null) {
            throw new RuntimeException("用户未找到,ID: " + userId);
        }
        payment.setBalance(payment.getBalance().subtract(amount));
        paymentDAO.update(payment);
    }
}

4. 数据库操作类

OrderDAO
@Repository
public class OrderDAO {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insert(Order order) {
        String sql = "INSERT INTO `order` (user_id, amount, status) VALUES (?, ?, 'CREATED')";
        jdbcTemplate.update(sql, order.getUserId(), order.getAmount());
    }
}
InventoryDAO
@Repository
public class InventoryDAO {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public Inventory findBySku(String sku) {
        String sql = "SELECT * FROM `inventory` WHERE sku = ?";
        return jdbcTemplate.queryForObject(sql, Inventory.class, sku);
    }

    public void update(Inventory inventory) {
        String sql = "UPDATE `inventory` SET stock = ? WHERE sku = ?";
        jdbcTemplate.update(sql, inventory.getStock(), inventory.getSku());
    }
}

5. 测试与验证

1. 触发全局事务

创建 OrderController 提供测试接口:

@RestController
@RequestMapping("/orders")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @PostMapping("/create")
    public String createOrder(@RequestBody Order order) {
        try {
            orderService.createOrder(order);
            return "订单创建成功!";
        } catch (Exception e) {
            return "订单创建失败:" + e.getMessage();
        }
    }
}
2. 模拟失败场景

PaymentService 中故意抛出异常:

public void charge(String userId, BigDecimal amount) {
    // 模拟支付失败
    if (Math.random() > 0.5) {
        throw new RuntimeException("支付失败!");
    }
    // 正常逻辑...
}
3. 验证回滚

成功场景:所有服务正常,订单提交成功。
失败场景:支付失败,触发全局回滚:
• 库存恢复(inventory 表的 stock 加回)。
• 订单状态保持为 CREATED
• 支付记录未提交(需手动回滚,由 Seata 自动处理)。


关键原理与优势

1. Seata AT模式的核心流程

阶段一(Branch Registration)
• TM(订单服务)启动全局事务,向 TC 注册分支事务。
阶段二(Local Transaction)
• 各个 RM(库存、支付、订单服务)执行本地事务,自动记录 Undo Log。
阶段三(Commit/Rollback)
• TC 收集所有分支事务状态,决定提交或回滚:
◦ 提交:各 RM 提交本地事务。
◦ 回滚:通过 Undo Log 恢复数据。

2. 优势对比传统2PC

无代码侵入:通过注解 @GlobalTransactional 简化开发。
高性能:基于乐观锁和异步通信,减少同步阻塞。
自动补偿:无需手动编写补偿逻辑(如 TCC 的 Cancel 方法)。


常见问题与解决

1. 事务未提交/回滚

检查点
• 确保 @GlobalTransactional 注解正确标注在 TM 方法上。
• 确认 Seata Server 和 Nacos 服务正常运行。

2. 数据不一致

解决方案
• 检查 undo_log 表是否正常记录回滚信息。
• 验证本地事务的 @Transactional 是否生效。

3. 超时问题

调整配置
• 在 application.yml 中设置 global.tx.timeout=30000(默认 30 秒)。
• 优化业务逻辑,减少事务执行时间。


总结

通过 Seata 的 AT 模式,我们无需手动实现复杂的 2PC 或 3PC 协议,即可轻松获得分布式事务的强一致性保障。其核心优势在于:
开发成本低:注解驱动,减少模板代码。
高性能:异步回滚机制,降低资源阻塞。
高可用:集群化部署,避免单点故障。

适用场景
• 电商订单、金融转账等需要强一致性的场景。
• 微服务架构中需跨服务事务的场景。

进阶方向
• 结合 Saga 模式处理长事务。
• 通过 Seata 的 @Compensable 注解实现自定义补偿逻辑。

相关文章:

  • 二叉树的层平均值
  • 企业信息化的“双螺旋”——IT治理和数据治理
  • 北京市大模型备案及登记分析报告
  • 【设计模式】SOLID 设计原则概述
  • oracle 索引
  • 【鸿蒙开发】Hi3861学习笔记- WIFI应用AP建立网络
  • Python---数据分析(Pandas六:二维数组DataFrame,DataFrame的创建,DataFrame的属性)
  • CSS实现当鼠标悬停在一个元素上时,另一个元素的样式发生变化的效果
  • 前端网络请求
  • 面向对象(进阶)(‘封装‘,‘多态‘,‘对象属性‘,‘类属性‘,‘类方法‘,‘对象方法‘及其应用场景)
  • 糊涂人寄信——递推
  • 算法设计与分析——动态规划
  • KnowGPT知识图谱整合
  • 深入浅出理解LLM PPO:基于verl框架的实现解析之一
  • Java并发编程面试题:锁(17题)
  • c++ 数组索引越界检查
  • 解决 C 盘空间不足,免费软件高效清理
  • Python 用户账户(创建用户账户)
  • GaussDB构建高性能Schema:分布式数据库架构设计与实战
  • python NameError报错之导库报错
  • 夜读丨什么样的前程值得把春天错过
  • 美国务卿会见叙利亚外长,沙特等国表示将支持叙利亚重建
  • 自然资源部:不动产登记累计化解遗留问题房屋2000多万套
  • 证监会强化上市公司募资监管七要点:超募资金不得补流、还贷
  • 特朗普中东行:“能源换科技”背后的权力博弈|907编辑部
  • 不是10点!乌克兰官员称尚未就俄乌谈判开始时间达成一致