源本科技 | 码上会

分布式事务 Seata 创建测试服务

2026/05/22
2
0

引言

在微服务架构的设计理念中,每个微服务拥有独立的专属数据库是核心设计原则,能够实现服务与数据的解耦。但跨多个独立数据库的业务操作,会引发分布式事务问题:无法通过传统本地事务保证多个数据库操作的原子性(要么全部成功,要么全部失败)。

业务场景

以电子商务系统为案例,用户创建订单的流程包含两个核心操作:

  • 订单服务向订单数据库写入订单数据

  • 库存服务向库存数据库扣减商品库存

两个操作分布在不同的数据库中,必须在同一个分布式事务中执行,确保数据最终一致性。

环境准备

项目目录结构

创建根目录 sca-service 统一管理业务微服务,内部包含两个核心子模块:

  • sca-service-order:订单服务(服务消费者,调用库存服务)

  • sca-service-stock:库存服务(服务提供者,提供库存扣减接口)

依赖版本管理

项目基于 Spring Boot 3 构建,使用统一的依赖管理模块管控版本,核心依赖版本如下:

  • sca-dependencies\pom.xml 追加

<properties>
    <mybatis-plus.version>3.5.15</mybatis-plus.version>
</properties>
  • 在公共依赖模块统一引入:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>${mybatis-plus.version}</version>
</dependency>

部署独立数据库

使用 Docker 部署两个相互隔离的 MySQL 8 实例,分别对应订单服务、库存服务。

订单服务数据库

sca-service/order 目录下创建 docker-compose.yml 配置文件:

services:
  mysql:
    image: mysql:8.0.30
    container_name: mysql-order
    restart: always
    volumes:
      - ./data/mysql/data:/var/lib/mysql
    environment:
      - TZ=Asia/Shanghai
      - MYSQL_ROOT_PASSWORD=123456
    command:
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --explicit_defaults_for_timestamp=true
      --lower_case_table_names=1
    ports:
      - 3307:3306

执行 SQL 初始化数据库和表(数据库名使用下划线规范,避免横杠语法异常):

-- 创建数据库
CREATE DATABASE sca_order DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

USE sca_order;
-- 订单表
CREATE TABLE `t_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `product_id` int(11) DEFAULT NULL,
  `count` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

注意:连接订单数据库时,端口为 3307,为独立隔离的数据库实例

image-VElT.webp

库存服务数据库

sca-service/stock 目录下创建 docker-compose.yml 配置文件:

services:
  mysql:
    image: mysql:8.0.30
    container_name: mysql-stock
    restart: always
    volumes:
      - ./data/mysql/data:/var/lib/mysql
    environment:
      - TZ=Asia/Shanghai
      - MYSQL_ROOT_PASSWORD=123456
    command:
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --explicit_defaults_for_timestamp=true
      --lower_case_table_names=1
    ports:
      - 3308:3306

执行 SQL 初始化数据库、表和测试数据:

-- 创建数据库
CREATE DATABASE sca_stock DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

USE sca_stock;
-- 库存表
CREATE TABLE `t_stock` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_id` int(11) DEFAULT NULL,
  `count` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

-- 初始化测试数据:商品 1 的库存为 100
INSERT INTO t_stock (product_id, `count`) VALUES (1, 100);

注意:连接库存数据库时,端口为 3308,为独立隔离的数据库实例

微服务搭建

库存服务实现

sca-service 模块下创建 sca-service-stock 库存微服务,作为服务提供者

核心 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.lusifer</groupId>
        <artifactId>sca-service</artifactId>
        <version>${revision}</version>
    </parent>

    <artifactId>sca-service-stock</artifactId>
    <packaging>jar</packaging>
    <name>${project.artifactId}</name>
    <description>库存服务</description>

    <dependencies>
        <!-- Web 核心依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- 服务治理 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <!-- 数据持久化 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        </dependency>

        <!-- 工具类 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 分布式事务 Seata -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        </dependency>

        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
    </build>
</project>

订单服务实现

sca-service 模块下创建 sca-service-order 订单微服务,作为服务消费者

核心 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.lusifer</groupId>
        <artifactId>sca-service</artifactId>
        <version>${revision}</version>
    </parent>

    <artifactId>sca-service-order</artifactId>
    <packaging>jar</packaging>
    <name>${project.artifactId}</name>
    <description>订单服务</description>

    <dependencies>
        <!-- Web 核心依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- 远程调用 + 负载均衡 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

        <!-- 服务治理 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <!-- 数据持久化 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        </dependency>

        <!-- 工具类 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 分布式事务 Seata -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        </dependency>

        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
    </build>
</project>