🎓博主介绍:Java、Python、js全栈开发 “多面手”,精通多种编程语言和技术,痴迷于人工智能领域。秉持着对技术的热爱与执着,持续探索创新,愿在此分享交流和学习,与大家共进步。
📖DeepSeek-行业融合之万象视界(附实战案例详解100+)
📖全栈开发环境搭建运行攻略:多语言一站式指南(环境搭建+运行+调试+发布+保姆级详解)
👉感兴趣的可以先收藏起来,希望帮助更多的人
在这里插入图片描述

分布式事务难题破解:SpringBoot+Seata实现AT模式全流程解析

一、引言

在当今的互联网应用中,分布式系统已经成为主流架构。随着业务的不断发展和系统的不断拆分,分布式事务问题变得愈发突出。分布式事务是指在分布式系统中,多个服务或数据库之间需要保证数据的一致性和完整性。解决分布式事务问题是构建可靠、稳定的分布式系统的关键。

Seata(Simple Extensible Autonomous Transaction Architecture)是一款开源的分布式事务解决方案,提供了 AT、TCC、SAGA 和 XA 等多种事务模式。本文将详细介绍如何使用 Spring Boot 和 Seata 实现 AT 模式的分布式事务,帮助技术人员深入理解和掌握这一解决方案。

二、分布式事务概述

2.1 分布式事务产生的背景

随着互联网业务的快速发展,单体应用逐渐无法满足业务的需求,于是出现了分布式系统架构。在分布式系统中,业务操作往往会涉及多个服务和数据库,每个服务和数据库都有自己的事务管理机制。例如,一个电商系统中的订单服务、库存服务和支付服务可能分别部署在不同的服务器上,当用户下单时,需要同时更新订单表、扣减库存和完成支付,这就涉及到多个服务之间的事务一致性问题。

2.2 常见的分布式事务解决方案

  • 两阶段提交(2PC):是一种经典的分布式事务解决方案,分为准备阶段和提交阶段。但它存在性能问题和单点故障问题。
  • 三阶段提交(3PC):在 2PC 的基础上增加了一个预准备阶段,一定程度上缓解了 2PC 的单点故障问题,但性能仍然不理想。
  • TCC(Try-Confirm-Cancel):将一个业务操作拆分为 Try、Confirm 和 Cancel 三个操作,通过这三个操作来保证事务的一致性。
  • SAGA:将一个长事务拆分为多个短事务,每个短事务都有自己的补偿操作,当某个短事务失败时,通过执行补偿操作来保证数据的一致性。

2.3 Seata AT 模式的优势

Seata 的 AT 模式是一种无侵入的分布式事务解决方案,它基于数据库的本地事务和全局事务协调器(TC)来实现分布式事务的管理。AT 模式的主要优势包括:

  • 无侵入性:业务代码无需做任何修改,只需要添加 Seata 的注解即可。
  • 高性能:AT 模式在执行过程中,只需要在业务操作前后记录相关的日志,不会对业务操作产生太大的性能影响。
  • 易用性:Seata 提供了简单易用的 API 和注解,开发人员可以快速上手。

三、Seata 基础介绍

3.1 Seata 架构

Seata 主要由三个组件组成:

  • 事务协调器(TC,Transaction Coordinator):维护全局事务的状态,协调并驱动全局事务的提交或回滚。
  • 事务管理器(TM,Transaction Manager):定义全局事务的范围,开始全局事务、提交或回滚全局事务。
  • 资源管理器(RM,Resource Manager):管理本地资源,负责分支事务的提交和回滚。

3.2 Seata AT 模式原理

AT 模式的执行过程主要分为两个阶段:

  • 一阶段:业务 SQL 执行前,Seata 会自动生成回滚日志,记录业务数据在更新前的状态。业务 SQL 执行后,Seata 会记录业务数据在更新后的状态。然后,本地事务提交,释放本地锁和连接资源。
  • 二阶段
    • 提交:如果全局事务决定提交,TC 会通知 RM 异步删除一阶段记录的回滚日志。
    • 回滚:如果全局事务决定回滚,TC 会通知 RM 根据一阶段记录的回滚日志进行反向补偿操作,将数据恢复到更新前的状态。

四、Spring Boot 集成 Seata AT 模式的环境搭建

4.1 环境准备

  • JDK 1.8 及以上
  • Maven 3.x
  • MySQL 5.7 及以上
  • Seata Server 1.4.x 及以上

4.2 搭建 Seata Server

  1. 下载 Seata Server:从 Seata 的 GitHub 仓库(https://github.com/seata/seata/releases)下载最新版本的 Seata Server。
  2. 解压并配置:解压下载的文件,进入 conf 目录,修改 file.confregistry.conf 文件。
    • file.conf 配置示例:
service {
  vgroupMapping.my_test_tx_group = "default"
  default.grouplist = "127.0.0.1:8091"
  enableDegrade = false
  disableGlobalTransaction = false
}

store {
  mode = "db"
  db {
    datasource = "druid"
    dbType = "mysql"
    driverClassName = "com.mysql.cj.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true"
    user = "root"
    password = "root"
    minConn = 5
    maxConn = 30
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }
}
  • registry.conf 配置示例:
registry {
  type = "file"
  file {
    name = "file.conf"
  }
}

config {
  type = "file"
  file {
    name = "file.conf"
  }
}
  1. 创建数据库和表:创建一个名为 seata 的数据库,并执行 conf/db_store.sql 文件中的 SQL 语句,创建相关的表。
  2. 启动 Seata Server:进入 bin 目录,执行 seata-server.sh(Linux/Mac)或 seata-server.bat(Windows)启动 Seata Server。

4.3 创建 Spring Boot 项目

使用 Spring Initializr(https://start.spring.io/)创建一个 Spring Boot 项目,添加以下依赖:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
  </dependency>
  <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <version>2.2.5.RELEASE</version>
  </dependency>
</dependencies>

4.4 配置 Seata

application.yml 中添加 Seata 相关配置:

spring:
  cloud:
    alibaba:
      seata:
        tx-service-group: my_test_tx_group
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&rewriteBatchedStatements=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

五、Spring Boot+Seata AT 模式代码实现

5.1 数据库表设计

创建两个数据库表 orderstock,分别用于存储订单信息和库存信息。

-- 订单表
CREATE TABLE `order` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL,
  `product_id` bigint(20) NOT NULL,
  `count` int(11) NOT NULL,
  `amount` decimal(10, 2) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 库存表
CREATE TABLE `stock` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `product_id` bigint(20) NOT NULL,
  `total` int(11) NOT NULL,
  `used` int(11) NOT NULL,
  `residue` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

5.2 实体类定义

创建 OrderStock 实体类:

// Order.java
public class Order {
    private Long id;
    private Long userId;
    private Long productId;
    private Integer count;
    private BigDecimal amount;

    // 省略 getter 和 setter 方法
}

// Stock.java
public class Stock {
    private Long id;
    private Long productId;
    private Integer total;
    private Integer used;
    private Integer residue;

    // 省略 getter 和 setter 方法
}

5.3 DAO 层实现

使用 Spring JDBC Template 实现 OrderDaoStockDao

// OrderDao.java
@Repository
public class OrderDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void createOrder(Order order) {
        String sql = "INSERT INTO `order` (user_id, product_id, count, amount) VALUES (?,?,?,?)";
        jdbcTemplate.update(sql, order.getUserId(), order.getProductId(), order.getCount(), order.getAmount());
    }
}

// StockDao.java
@Repository
public class StockDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void decreaseStock(Long productId, Integer count) {
        String sql = "UPDATE stock SET total = total - ?, used = used + ?, residue = residue - ? WHERE product_id = ?";
        jdbcTemplate.update(sql, count, count, count, productId);
    }
}

5.4 Service 层实现

OrderService 中使用 @GlobalTransactional 注解开启全局事务:

@Service
public class OrderService {
    @Autowired
    private OrderDao orderDao;
    @Autowired
    private StockDao stockDao;

    @GlobalTransactional(name = "my_test_tx_group", rollbackFor = Exception.class)
    public void createOrder(Long userId, Long productId, Integer count, BigDecimal amount) {
        // 创建订单
        Order order = new Order();
        order.setUserId(userId);
        order.setProductId(productId);
        order.setCount(count);
        order.setAmount(amount);
        orderDao.createOrder(order);

        // 扣减库存
        stockDao.decreaseStock(productId, count);

        // 模拟异常
        if (true) {
            throw new RuntimeException("模拟异常,触发全局事务回滚");
        }
    }
}

5.5 Controller 层实现

创建 OrderController 提供接口调用:

@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    private OrderService orderService;

    @GetMapping("/create")
    public String createOrder(Long userId, Long productId, Integer count, BigDecimal amount) {
        try {
            orderService.createOrder(userId, productId, count, amount);
            return "订单创建成功";
        } catch (Exception e) {
            return "订单创建失败:" + e.getMessage();
        }
    }
}

六、测试与验证

6.1 启动项目

启动 Spring Boot 项目和 Seata Server。

6.2 调用接口

使用 Postman 或其他工具调用 http://localhost:8080/order/create?userId=1&productId=1&count=10&amount=100.00 接口。

6.3 验证结果

由于在 OrderService 中模拟了异常,全局事务会回滚,数据库中的订单表和库存表数据不会发生变化。

七、总结与展望

7.1 总结

本文详细介绍了如何使用 Spring Boot 和 Seata 实现 AT 模式的分布式事务。通过 Seata 的 AT 模式,我们可以在不修改业务代码的情况下,轻松解决分布式系统中的事务一致性问题。同时,Seata 的高性能和易用性也为开发人员提供了极大的便利。

7.2 展望

随着分布式系统的不断发展,分布式事务解决方案也将不断完善。未来,Seata 可能会提供更多的事务模式和功能,以满足不同场景的需求。同时,我们也可以将 Seata 与其他技术(如微服务框架、消息队列等)结合使用,构建更加复杂和可靠的分布式系统。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐