大家好,上一篇我们完成了微服务容器化部署(Docker+Compose),通过镜像打包和一键编排,彻底解决了“部署繁琐、环境不一致”的痛点,让微服务集群具备了可落地的部署能力。但在微服务实际运行中,还有一个致命痛点的未解决——跨服务数据一致性:比如“下单扣积分”场景,order-service成功创建订单,但user-service扣减积分时失败(服务宕机、网络异常),会导致“订单已创建、积分未扣减”的脏数据;再比如“下单扣库存”,订单创建成功但库存扣减失败,会导致超卖问题。今天就聚焦这个核心痛点,手把手教大家集成Seata实现分布式事务,解决跨服务数据不一致问题,全程实操、新手友好,跟着走就能掌握!

温馨提示:实操前请确认前提(避免踩坑):
① 已完成前10篇实操,微服务已实现Docker+Compose容器化部署,所有服务能正常通信;
② 版本保持一致:Spring Boot 2.6.13 + Spring Cloud Hoxton.SR12 + Nacos 2.0.7 + Seata 1.5.2(与Spring Boot 2.6.x兼容,新手必选此版本);
③ 已安装MySQL(用于Seata持久化和微服务业务数据);
④ 重点注意:本次实操基于Seata最易上手的AT模式,无需修改大量业务代码,仅需添加依赖、配置和注解;
⑤ 建议提前备份微服务代码和数据库数据(避免实操中数据混乱)。

一、先搞懂:分布式事务,微服务落地的必跨门槛

前10篇我们实现的微服务通信(OpenFeign),本质是“跨服务的远程调用”,每个微服务都有自己独立的数据库(比如order-service操作订单库,user-service操作用户库),这就导致了“分布式事务”问题——传统的本地事务(@Transactional)只能保证单个微服务内的数据一致性,无法覆盖跨服务的多个数据库操作。
核心痛点:跨服务数据不一致案例(新手直观理解)
我们以“下单扣积分”为实操场景(贴合前序微服务业务),模拟无分布式事务时的脏数据问题:

  1. 业务流程:用户发起下单请求 → gateway转发到order-service → order-service创建订单(操作订单库) → order-service通过Feign调用user-service → user-service扣减用户积分(操作用户库);

  2. 异常场景:order-service成功创建订单(本地事务提交,订单库数据已更新),但调用user-service时出现网络异常/服务宕机,导致user-service扣减积分失败(本地事务回滚,用户库数据未更新);

  3. 最终结果:订单已创建、积分未扣减,形成脏数据,违背业务逻辑,若不解决,会导致用户重复享受积分、系统数据混乱,严重影响业务正常运行。

类似的场景还有很多:下单扣库存、支付回调同步订单状态、转账业务等,只要涉及“跨服务、多数据库操作”,就会存在分布式事务问题。而Seata正是解决这个问题的主流方案,专为微服务分布式事务设计,简单易用、性能优异。

核心概念拆解(新手必记,避免混淆,拒绝复杂术语)
Seata的核心设计非常简单,核心分为3个角色,记住“1个协调者+多个参与者”的逻辑即可:

  1. 事务协调器(TC,Transaction Coordinator):独立的Seata服务端,核心职责是“协调所有参与者”——记录全局事务状态、发送提交/回滚指令,确保所有参与者要么一起提交,要么一起回滚;

  2. 事务管理器(TM,Transaction Manager):发起全局事务的微服务(比如本次场景中的order-service,因为它是第一个执行操作、并调用其他服务的入口),核心职责是“开启全局事务、通知TC提交/回滚”;

  3. 资源管理器(RM,Resource Manager):参与全局事务的所有微服务(比如本次场景中的order-service、user-service),核心职责是“执行本地事务、接收TC的指令、执行提交/回滚”;

补充:Seata AT模式核心原理(新手无需深入源码,记住结论即可):

AT模式是Seata默认、最常用的模式,无需手动编写回滚逻辑,核心分为两步:① 执行阶段:各微服务执行本地事务,记录数据修改前的快照(undo_log表),提交本地事务,释放锁;② 提交/回滚阶段:若所有微服务执行成功,TC通知所有RM提交(删除undo_log快照);若有一个微服务失败,TC通知所有RM回滚(根据undo_log快照恢复数据)。

补充:为什么选择Seata AT模式?(新手必看)

  1. 无侵入性:无需修改业务代码,仅需添加注解(@GlobalTransactional)和配置,新手易上手;

  2. 性能优异:执行阶段提交本地事务,释放锁,避免长时间锁表,适配高并发场景;

  3. 兼容性好:与Spring Cloud、Nacos、MySQL无缝兼容,贴合我们前10篇的微服务体系;

  4. 成熟稳定:阿里开源,广泛应用于生产环境,解决微服务分布式事务的主流方案。

二、实操核心:Seata AT模式实现分布式事务(下单扣积分场景)

本篇实操完全贴合前序微服务场景,以“order-service创建订单+user-service扣减积分”为核心,分为四大模块、9个核心步骤,从Seata服务端搭建到微服务集成,再到效果测试,全程落地:

模块一:环境准备(2步)→ 模块二:搭建Seata服务端(TC)(2步)→ 模块三:微服务集成Seata客户端(3步)→ 模块四:测试分布式事务效果(2步)

重点说明:
① 本次实操基于Docker+Compose部署Seata服务端,也可本地部署(新手任选);
② 核心是实现“订单创建”和“积分扣减”的原子性——要么都成功,要么都失败;
③ 所有配置均贴合之前的微服务版本,避免版本冲突。

三、模块一:环境准备(核心前提,必做)

3.1 第一步:创建Seata所需数据库和表

Seata服务端(TC)需要数据库存储全局事务信息,同时所有参与分布式事务的微服务(RM)需要创建undo_log表(用于数据回滚快照),步骤如下:

  1. 新建数据库(用于Seata服务端):在MySQL中创建数据库,命名为seata_db(自定义,便于记忆);

  2. 执行Seata初始化SQL(创建全局事务表):复制以下SQL到MySQL执行(适配Seata 1.5.2版本):

-- 全局事务表(Seata服务端使用)
CREATE TABLE IF NOT EXISTS `global_table` (
  `xid` VARCHAR(128) NOT NULL COMMENT '全局事务ID',
  `transaction_id` BIGINT NULL DEFAULT NULL COMMENT '事务ID',
  `status` TINYINT NOT NULL COMMENT '状态:0-未提交,1-提交中,2-已提交,3-回滚中,4-已回滚',
  `application_id` VARCHAR(32) NULL DEFAULT NULL COMMENT '应用ID',
  `transaction_service_group` VARCHAR(32) NULL DEFAULT NULL COMMENT '事务组名称',
  `transaction_name` VARCHAR(128) NULL DEFAULT NULL COMMENT '事务名称',
  `timeout` INT NULL DEFAULT NULL COMMENT '超时时间',
  `begin_time` BIGINT NULL DEFAULT NULL COMMENT '开始时间',
  `application_data` VARCHAR(2000) NULL DEFAULT NULL COMMENT '应用数据',
  `gmt_create` DATETIME NULL DEFAULT NULL COMMENT '创建时间',
  `gmt_modified` DATETIME NULL DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`xid`),
  INDEX `idx_gmt_modified_status` (`gmt_modified`, `status`),
  INDEX `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='全局事务表';

-- 分支事务表(Seata服务端使用)
CREATE TABLE IF NOT EXISTS `branch_table` (
  `branch_id` BIGINT NOT NULL COMMENT '分支事务ID',
  `xid` VARCHAR(128) NOT NULL COMMENT '全局事务ID',
  `transaction_id` BIGINT NULL DEFAULT NULL COMMENT '事务ID',
  `resource_group_id` VARCHAR(32) NULL DEFAULT NULL COMMENT '资源组ID',
  `resource_id` VARCHAR(256) NULL DEFAULT NULL COMMENT '资源ID',
  `branch_type` VARCHAR(8) NULL DEFAULT NULL COMMENT '分支类型',
  `status` TINYINT NOT NULL COMMENT '状态:0-未提交,1-提交中,2-已提交,3-回滚中,4-已回滚',
  `client_id` VARCHAR(64) NULL DEFAULT NULL COMMENT '客户端ID',
  `application_data` VARCHAR(2000) NULL DEFAULT NULL COMMENT '应用数据',
  `gmt_create` DATETIME NULL DEFAULT NULL COMMENT '创建时间',
  `gmt_modified` DATETIME NULL DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`branch_id`),
  INDEX `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分支事务表';

-- 锁表(Seata服务端使用)
CREATE TABLE IF NOT EXISTS `lock_table` (
  `row_key` VARCHAR(128) NOT NULL COMMENT '行锁键',
  `xid` VARCHAR(128) NULL DEFAULT NULL COMMENT '全局事务ID',
  `transaction_id` BIGINT NULL DEFAULT NULL COMMENT '事务ID',
  `branch_id` BIGINT NOT NULL COMMENT '分支事务ID',
  `resource_id` VARCHAR(256) NULL DEFAULT NULL COMMENT '资源ID',
  `table_name` VARCHAR(32) NULL DEFAULT NULL COMMENT '表名',
  `pk` VARCHAR(36) NULL DEFAULT NULL COMMENT '主键',
  `gmt_create` DATETIME NULL DEFAULT NULL COMMENT '创建时间',
  `gmt_modified` DATETIME NULL DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`row_key`),
  INDEX `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='锁表';
  1. 给微服务数据库创建undo_log表:分别在order-service(订单库)、user-service(用户库)中执行以下SQL(RM回滚快照表):
--  undo_log表(所有参与分布式事务的微服务数据库都需要创建)
CREATE TABLE IF NOT EXISTS `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 '日志状态:0-正常,1-已清理',
  `log_created` DATETIME NOT NULL COMMENT '创建时间',
  `log_modified` DATETIME NOT NULL COMMENT '修改时间',
  UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`),
  INDEX `idx_log_created` (`log_created`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='回滚快照表';

3.2 第二步:配置Nacos,添加Seata配置(核心,实现服务发现)

Seata服务端(TC)需要注册到Nacos,微服务(TM/RM)也需要从Nacos获取Seata的地址,步骤如下:

  1. 打开Nacos控制台(http://localhost:8848/nacos),切换到dev命名空间、DEV_GROUP分组;

  2. 点击“配置管理→配置列表→+”,创建Seata的全局配置,配置信息如下:

  • 数据ID:seata-server-dev.yml(命名规范:服务名-环境.yml);

  • 分组:DEV_GROUP(和微服务分组一致);

  • 配置格式:YAML;

  • 配置内容(复制粘贴,修改MySQL地址/用户名/密码为自己的):

# Seata服务端配置(TC)
server:
  port: 8091  # Seata服务端默认端口

spring:
  application:
    name: seata-server
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        namespace: dev
        group: DEV_GROUP
        username: nacos
        password: nacos

# Seata核心配置
seata:
  # 事务组配置(核心,所有微服务需和此事务组一致)
  tx-service-group: springcloud-seata-group
  # 注册中心配置(Nacos)
  registry:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace: dev
      group: DEV_GROUP
      application: seata-server
      username: nacos
      password: nacos
  # 配置中心配置(Nacos)
  config:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace: dev
      group: DEV_GROUP
      username: nacos
      password: nacos
  # 存储配置(MySQL,用于存储全局事务信息)
  store:
    mode: db  # 存储模式:db(数据库),file(本地文件,不适合生产)
    db:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/seata_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
      user: root  # 自己的MySQL用户名
      password: 123456  # 自己的MySQL密码
      min-conn: 5
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      distributed-lock-table: distributed_lock
      query-limit: 100
      max-wait: 5000
  1. 点击“发布”,Seata的Nacos配置生效;
    易错点提醒:
    ① MySQL地址、用户名、密码必须修改为自己的,否则Seata服务端无法连接数据库;
    ② 事务组名称(springcloud-seata-group)后续微服务配置需和此一致,否则无法加入全局事务。

四、模块二:搭建Seata服务端(TC,事务协调器)

我们采用Docker+Compose部署Seata服务端(贴合上一篇容器化风格,一键启动,新手无需手动配置Seata环境),步骤如下:
4.1 第一步:修改Docker Compose配置,添加Seata服务
打开上一篇编写的docker-compose.yml文件,在services节点下添加Seata服务(放在Nacos服务之后,其他服务之前):

# 新增:Seata服务端(TC)
seata-server:
  image: seataio/seata-server:1.5.2  # Seata版本1.5.2,与Spring Boot 2.6.x兼容
  container_name: seata-server
  ports:
    - "8091:8091"  # Seata默认端口
  environment:
    - SEATA_CONFIG_NAME=file:/root/seata-config/seata-server-dev.yml
    - SEATA_IP=localhost  # 本地部署,填写localhost即可
  volumes:
    # 挂载Nacos中Seata的配置文件(映射到容器内)
    - ./seata-config:/root/seata-config
  depends_on:
    - nacos  # 依赖Nacos,Nacos启动后再启动Seata
  networks:
    - springcloud-network
  restart: always

4.2 第二步:启动Seata服务,验证部署成功

  1. 准备Seata配置文件挂载目录:在docker-compose.yml同级目录新建seata-config文件夹,将Nacos中seata-server-dev.yml的配置内容复制,在该文件夹下新建seata-server-dev.yml文件,粘贴配置(确保容器能挂载到配置);
  2. 进入docker-compose.yml所在目录,执行启动命令(一键启动Seata和其他服务):
# 后台启动所有服务(若已启动,执行此命令会重启新增的Seata服务)
docker compose up -d
  1. 验证Seata启动成功:
  • 查看容器状态:执行docker compose ps,确保seata-server状态为Up

  • 查看Nacos服务列表:访问Nacos控制台,服务列表中能看到seata-server,状态为“UP”;

  • 查看Seata日志:执行docker logs seata-server,无报错,显示“started successfully”,说明启动成功。

易错点提醒:
① Seata版本必须是1.5.2,与Spring Boot 2.6.x兼容,版本不匹配会导致微服务无法连接Seata;
② 配置文件挂载路径必须正确,否则Seata无法读取配置,启动失败。

五、模块三:微服务集成Seata客户端(TM+RM)

本次场景中,order-service是TM(发起全局事务),order-service和user-service都是RM(参与全局事务),gateway-service无需集成(不参与数据库操作),步骤如下(重点配置order和user服务):

5.1 第一步:给微服务添加Seata依赖(pom.xml)

分别给order-service和user-service添加Seata依赖(版本1.5.2,与Seata服务端一致),添加到dependencies标签内:

<!-- Seata客户端依赖 -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.5.2</version>
</dependency>
<!-- Seata与Nacos集成依赖(用于从Nacos获取Seata地址) -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-discovery-nacos</artifactId>
    <version>1.5.2</version>
</dependency>
<!-- Seata与MySQL集成依赖(用于undo_log表操作) -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-rm-datasource</artifactId>
    <version>1.5.2</version>
</dependency>

5.2 第二步:修改微服务Nacos配置,添加Seata客户端配置
分别在order-service-dev.yml和user-service-dev.yml中添加Seata客户端配置(放在配置末尾,复制粘贴即可,无需修改):

# Seata客户端配置(TM/RM)
seata:
  # 事务组名称,必须和Seata服务端的事务组一致
  tx-service-group: springcloud-seata-group
  # 服务发现配置(从Nacos获取Seata服务端地址)
  discovery:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace: dev
      group: DEV_GROUP
      application: seata-server
      username: nacos
      password: nacos
  # 配置中心配置(从Nacos获取Seata配置)
  config:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace: dev
      group: DEV_GROUP
      username: nacos
      password: nacos
  # 数据源代理配置(开启Seata数据源代理,用于生成undo_log快照)
  datasource:
    proxy-mode: AT  # 采用AT模式

易错点提醒:
① 事务组名称(springcloud-seata-group)必须和Seata服务端一致,否则微服务无法加入全局事务;
② 数据源代理模式必须设为AT,否则无法生成undo_log快照,回滚失败。

5.3 第三步:编写分布式事务代码(核心,添加注解即可)

Seata AT模式几乎无侵入性,仅需在TM(order-service)的全局事务入口方法上添加@GlobalTransactional注解,即可开启分布式事务,业务代码无需修改。

  1. 编写order-service业务代码(TM,全局事务入口):

打开order-service的OrderService类,在“创建订单+调用用户服务扣减积分”的方法上添加@GlobalTransactional注解:

package com.example.orderservice.service;

import com.example.orderservice.feign.UserFeignClient;
import com.example.orderservice.mapper.OrderMapper;
import com.example.orderservice.pojo.Order;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service
public class OrderService {

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private UserFeignClient userFeignClient;

    // 核心:添加@GlobalTransactional注解,开启全局事务
    // rollbackFor = Exception.class:所有异常都触发回滚
    @GlobalTransactional(rollbackFor = Exception.class)
    public String createOrder(Integer userId, String goodsName, Integer num) {
        try {
            // 1. 本地事务:创建订单(order-service操作订单库)
            Order order = new Order();
            order.setUserId(userId);
            order.setGoodsName(goodsName);
            order.setNum(num);
            order.setStatus(0); // 0-未支付,1-已支付
            orderMapper.insert(order);
            System.out.println("订单创建成功!订单ID:" + order.getId());

            // 2. 远程调用:调用user-service扣减积分(跨服务操作,user-service操作用户库)
            // 模拟扣减10积分(实际业务中积分数量可根据订单金额计算)
            String result = userFeignClient.deductPoints(userId, 10);
            System.out.println("积分扣减结果:" + result);

            return "订单创建成功!订单ID:" + order.getId() + ",积分扣减结果:" + result;
        } catch (Exception e) {
            // 若出现异常,全局事务回滚(订单创建回滚,积分扣减回滚)
            e.printStackTrace();
            throw new RuntimeException("订单创建失败,触发分布式事务回滚!");
        }
    }
}
  1. 编写user-service业务代码(RM,参与全局事务):
    打开user-service的UserService类,编写扣减积分的方法(本地事务,无需添加Seata注解,Seata会自动代理):
package com.example.userservice.service;

import com.example.userservice.mapper.UserMapper;
import com.example.userservice.pojo.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;

@Service
public class UserService {

    @Resource
    private UserMapper userMapper;

    // 本地事务:扣减积分,Seata会自动将其纳入全局事务
    @Transactional(rollbackFor = Exception.class)
    public String deductPoints(Integer userId, Integer points) {
        // 1. 查询用户信息
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在!");
        }
        // 2. 校验积分是否充足
        if (user.getPoints() < points) {
            throw new RuntimeException("积分不足,扣减失败!");
        }
        // 3. 扣减积分(本地事务操作)
        user.setPoints(user.getPoints() - points);
        userMapper.updateById(user);
        return "用户ID:" + userId + ",积分扣减成功!当前剩余积分:" + user.getPoints();
    }
}
  1. 编写Feign接口(order-service调用user-service):
    在order-service的UserFeignClient中添加扣减积分的接口:
package com.example.orderservice.feign;

import com.example.orderservice.fallback.UserFeignFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "user-service", fallback = UserFeignFallback.class)
public interface UserFeignClient {

    // 新增:扣减积分的Feign接口
    @GetMapping("/user/deductPoints")
    String deductPoints(@RequestParam("userId") Integer userId, @RequestParam("points") Integer points);

    // 之前的获取用户信息接口(保留)
    @GetMapping("/user/getInfo")
    String getUserInfo(@RequestParam("id") Integer id);
}
  1. 编写user-service接口(供Feign调用):
    在user-service的UserController中添加扣减积分的接口:
package com.example.userservice.controller;

import com.example.userservice.service.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;

@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService;

    // 新增:扣减积分接口(供order-service调用)
    @GetMapping("/deductPoints")
    public String deductPoints(@RequestParam("userId") Integer userId, @RequestParam("points") Integer points) {
        return userService.deductPoints(userId, points);
    }

    // 之前的获取用户信息接口(保留)
    @GetMapping("/getInfo")
    public String getUserInfo(@RequestParam("id") Integer id) {
        return userService.getUserInfo(id);
    }
}
  1. 重新打包微服务镜像,重启容器:
    分别进入order-service和user-service根目录,重新打包jar包、构建镜像,然后重启容器:
# 1. 重新打包微服务
mvn clean package -DskipTests

# 2. 重新构建镜像(覆盖原有镜像)
docker build -t springcloud/order-service:1.0 .
docker build -t springcloud/user-service:1.0 .

# 3. 重启容器(一键重启所有服务)
docker compose restart

六、模块四:测试分布式事务效果(核心,验证原子性)

我们分两种场景测试:
① 正常场景(所有服务执行成功);
② 异常场景(某服务执行失败),验证分布式事务的原子性——要么都成功,要么都失败。

6.1 场景一:正常场景(积分充足,所有操作成功)

  1. 准备数据:在user-service的用户表中,给userId=1的用户设置积分(比如100分);

  2. 发起请求:用Postman访问order-service的创建订单接口(可通过Gateway转发,地址:http://localhost:8080/gateway/order/create?userId=1&goodsName=手机&num=1);

  3. 预期结果:

  • 接口返回成功:“订单创建成功!订单ID:xxx,积分扣减结果:用户ID:1,积分扣减成功!当前剩余积分:90”;

  • 查看数据库:订单库新增一条订单记录,用户库userId=1的用户积分从100变为90;

  • 查看undo_log表:order库和user库的undo_log表有记录(执行成功后,Seata会自动删除快照,或标记为已清理)。

6.2 场景二:异常场景(积分不足,触发回滚)

  1. 准备数据:将userId=1的用户积分修改为5分(小于要扣减的10分);

  2. 发起请求:再次访问上述创建订单接口;

  3. 预期结果(核心验证回滚):

  • 接口返回失败:“订单创建失败,触发分布式事务回滚!”;

  • 查看数据库:订单库无新增订单记录(order-service的本地事务回滚),用户库userId=1的用户积分仍为5分(user-service的本地事务回滚);

  • 查看Seata日志:执行docker logs seata-server,能看到“全局事务回滚”的日志;

  • 查看undo_log表:order库和user库的undo_log表有快照记录(用于回滚,回滚后可手动清理或配置自动清理)。

补充测试:模拟网络异常——停止user-service容器,发起下单请求,同样会触发全局事务回滚,订单创建失败,确保跨服务异常的原子性。

七、实操总结与易错点复盘

本篇我们完成了Seata AT模式分布式事务的核心实操,成功解决了“跨服务数据一致性”的痛点,实现了“订单创建+积分扣减”的原子性,核心收获如下:

  1. Seata核心架构:
  • 核心角色:TC(事务协调器,Seata服务端)、TM(全局事务发起者,order-service)、RM(事务参与者,order-service、user-service);

  • 核心流程:开启全局事务(TM)→ 执行本地事务(RM)→ 全局提交/回滚(TC协调);

  1. 实操核心步骤:
  • 环境准备:创建Seata数据库和undo_log表、配置Nacos全局配置;

  • 服务端搭建:Docker+Compose部署Seata TC,注册到Nacos;

  • 客户端集成:添加Seata依赖、配置事务组和Nacos地址、在TM入口添加@GlobalTransactional注解;

  1. 核心价值:解决跨服务数据不一致问题,避免脏数据,筑牢微服务生产环境的数据安全防线,适配下单、支付等核心业务场景。

新手必看易错点(重点避坑,高频问题)

  1. 版本冲突:Seata服务端和客户端版本必须一致(均为1.5.2),否则微服务无法连接Seata,或回滚失败;

  2. 事务组名称不一致:微服务客户端的tx-service-group必须和Seata服务端一致,否则无法加入全局事务;

  3. 未创建undo_log表:参与分布式事务的微服务数据库未创建undo_log表,导致无法生成回滚快照,回滚失败;

  4. Seata数据库配置错误:Seata服务端的MySQL地址、用户名、密码错误,导致无法存储全局事务信息,启动失败;

  5. 未开启数据源代理:微服务客户端未配置seata.datasource.proxy-mode=AT,导致Seata无法代理数据源,无法生成undo_log;

  6. 注解添加错误:@GlobalTransactional注解添加到了非全局事务入口方法(比如添加到user-service的方法),导致无法开启全局事务;

  7. 微服务未重启:修改Seata配置或添加依赖后,未重新打包镜像、重启容器,导致配置不生效。

八、下一篇预告

本篇我们解决了微服务跨服务数据一致性的核心痛点,通过Seata AT模式实现了分布式事务,让微服务核心业务(如下单、支付)具备了数据安全性。下一篇将讲解微服务进阶的另一核心优化——高级链路追踪(SkyWalking)实操,替代之前的Zipkin,教大家实现更强大的链路分析、性能监控和慢查询定位,解决Zipkin“仅链路追踪、无性能分析”的不足,进一步提升微服务的可观测性和故障排查效率!

如果这篇实操教程对你有帮助,欢迎点赞、收藏、关注,实操过程中遇到任何问题(比如Seata启动失败、分布式事务回滚无效、微服务无法连接Seata),都可以在评论区留言,我会逐一回复,帮大家解决新手踩坑问题~

补充:需要本篇实操的完整代码(Seata配置文件、微服务业务代码、Feign接口、SQL脚本),评论区扣“Seata分布式事务代码”,我会分享百度网盘链接,方便大家对照学习、直接运行!

Logo

更多推荐