分布式事务解决方案:最佳实践(新手友好教程)

FastAPI跑起来
2025-06-18 21:13
阅读 787

开篇:为什么我们需要分布式事务?

开篇:为什么我们需要分布式事务?

在现代的软件开发中,尤其是互联网产品中,系统越来越复杂,数据存储也分散在多个服务里。 比如你使用一个电商网站下单的时候,可能涉及到“支付服务”、“订单服务”、“库存服务”等多个子系统。这时候,如果我们只在一个系统里操作没问题,但一旦涉及多个系统的数据修改,就可能出现一个问题:

比如用户已经付款了,结果库存没减少;或者订单生成失败,但是钱已经被扣了。

这就引出了我们今天要讲的重点——分布式事务

什么是分布式事务?

通俗地理解:在多个系统(服务)中完成一组相关操作,并保证这些操作要么全部成功、要么全部失败,这叫分布式事务。

它就是为了解决“跨服务”的数据一致性问题。


环境准备:先搭好你的编程环境

环境准备:先搭好你的编程环境

开始写代码之前,我们要准备好基本的开发环境。以下是一个最简单的配置建议。

所需工具清单:

  • Java 11 或以上版本
  • Maven 构建工具
  • Spring Boot 2.7+
  • MySQL 数据库
  • Seata(开源的分布式事务中间件)
  • IntelliJ IDEA(或其他你喜欢的 IDE)

安装步骤简述:

  1. 安装 Java

    • 前往 Oracle官网 或使用 OpenJDK 下载并安装
    • 配置 JAVA_HOME 环境变量
  2. 安装 MySQL

    • 推荐使用 XAMPP 或直接下载安装包安装
    • 创建数据库和用户,记下用户名和密码
  3. 安装 Seata Server

    • GitHub 地址:https://github.com/seata/seata
    • 下载最新的 release 版本,例如:v1.6.x
    • 解压后执行启动命令:
      cd seata-server/bin
      sh seata-server.sh -p 8091 -m db
      
    • 注意:需要提前创建数据库并导入 sql 文件(见 Seata 文档说明)
  4. IDE 安装

    • 推荐使用 IntelliJ IDEA(社区版即可)
    • 安装插件:Maven、Lombok、Spring Boot Helper

核心概念讲解:从头说清楚关键技术

核心概念讲解:从头说清楚关键技术

分布式事务的核心思想是:让多个服务协调完成一次完整的业务动作,并保持一致性。 为了做到这一点,有多种技术方案可以选择。

常见的分布式事务方案有哪些?

方案 中文名称 特点 使用场景
TCC Try-Confirm-Cancel 强一致、实现较复杂 对一致性要求高、性能要求一般的场景
SAGA 事件驱动模式 易实现、最终一致性 可接受数据短时间不一致的业务
XA 两阶段提交 全局锁,性能差 已经逐渐被替代
SEATA 阿里开源的中间件 支持AT、TCC、SAGA等多种模式 推荐新手入门首选

我们今天以 Seata + AT 模式 为例来学习,因为它最容易上手,且可以自动处理大部分事情。


Seata 的核心组件:

  • TC(Transaction Coordinator):事务协调者,Seata Server 角色
  • TM(Transaction Manager):事务管理器,通常是业务模块自己
  • RM(Resource Manager):资源管理器,管理本地事务与 TC 的通信

你可以把它们想象成一场会议中的三个角色:

  • TM 是主持人(主业务逻辑)
  • RM 是参会人(各服务)
  • TC 是会议组织者,确保所有人都统一行动(都提交或都回滚)

实战项目:用 Seata 实现一个电商订单系统

实战项目:用 Seata 实现一个电商订单系统

我们来做一个实际的小项目:用户下单 -> 扣减库存 -> 扣款 -> 下单成功

我们将模拟两个微服务:

  • 订单服务(Order Service)
  • 库存服务(Stock Service)

这两个服务分别连接各自的数据库,但我们希望他们一起参与同一个事务。


第一步:创建两个 Spring Boot 工程

使用 IntelliJ IDEA 的 Spring Initializr 功能,分别创建两个服务:

OrderService 工程结构:

order-service
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com.example.order
        │       ├── OrderApplication.java
        │       └── controller
        │           └── OrderController.java
        └── resources
            └── application.yml

StockService 工程结构类似:

stock-service
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com.example.stock
        │       ├── StockApplication.java
        │       └── controller
        │           └── StockController.java
        └── resources
            └── application.yml

第二步:添加 Seata 依赖

在两个项目的 pom.xml 文件中增加 Seata Starter 依赖:

<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.6.1</version>
</dependency>

第三步:配置 Seata 客户端

在每个项目的 application.yml 中添加如下配置(注意填写正确的 IP 和 port):

seata:
  enabled: true
  application-id: order-service # 填当前服务名
  tx-service-group: default_tx_group
  service:
    vgroup-mapping:
      default_tx_group: default
  config:
    type: file
    file:
      name: classpath:file.conf
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      data-id: seataServer.properties

第四步:初始化 Seata 的配置文件

复制 seata-server/conf/file.conf 到你的类路径下,例如:

src/main/resources/file.conf

这个配置文件定义了事务日志存储方式和数据源信息。


第五步:编写核心代码

OrderController.java 示例:

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

    @Autowired
    private OrderService orderService;

    @PostMapping("/create")
    @GlobalTransactional // 关键注解:开启全局事务
    public String createOrder() {
        try {
            orderService.createOrder();
            return "订单创建成功";
        } catch (Exception e) {
            return "订单创建失败:" + e.getMessage();
        }
    }
}

数据流转过程-1

OrderService.java 示例:

@Service
public class OrderService {

    @Autowired
    private RestTemplate restTemplate;

    public void createOrder() {
        // 调用库存服务,扣库存
        String stockResult = restTemplate.getForObject("http://localhost:8082/stocks/reduce", String.class);
        if (!"success".equals(stockResult)) {
            throw new RuntimeException("库存扣减失败");
        }

        // 模拟业务异常
        // throw new RuntimeException("故意出错");

        System.out.println("订单已创建");
    }
}

StockController.java 示例:

@RestController
@RequestMapping("/stocks")
public class StockController {

    @GetMapping("/reduce")
    public String reduceStock() {
        // 模拟数据库操作:库存减1
        System.out.println("库存减少成功");
        return "success";
    }
}

第六步:运行测试

  1. 启动 Seata Server
  2. 启动两个服务(订单和库存),访问接口:
    POST http://localhost:8081/orders/create
    

如果你在 OrderService 中手动抛出异常:

throw new RuntimeException("故意出错");

你会发现 库存没有被扣减,Seata 帮助你实现了回滚!


常见问题解答:你可能会遇到的问题都在这里

Q1:调用服务返回 connection refused 错误怎么办?

答:请检查服务是否正常启动,端口是否正确,以及是否加了 @LoadBalanced 注解(如果使用 Nacos 等注册中心的话)。

Q2:报错“Branch session not found”,该怎么解决?

答:通常发生在分布式事务中断或超时,可尝试提高事务等待时间和重试机制。

Q3:如何查看事务是否成功提交?

答:可以通过 Seata 提供的 UI 控制台(官方有提供 dashboard 插件)来查看事务状态。

Q4:AT 模式适合什么业务场景?

答:适用于大多数不需要手动写补偿逻辑的业务,对开发人员友好,是目前 Seata 的推荐模式。


学习建议:接下来你可以这样继续深入

恭喜你完成了第一个分布式事务的小项目!这是个很好的开始,但还有很多值得探索的内容:

初级进阶方向:

  • 理解 Seata 的 AT、TCC、SAGA 三种模式的区别
  • 尝试使用 Nacos 作为注册中心和配置中心
  • 深入 Seata 的 undo_log 表原理,理解它是如何进行回滚的

中高级路线:

  • 学习使用 Apache RocketMQ 实现基于消息队列的事务
  • 探索 Saga 模式在长流程业务中的应用
  • 掌握如何在生产环境中部署 Seata 并监控其运行状态

总结

在本篇文章中,我们从零基础出发,一步步搭建了一个基于 Seata 的分布式事务项目。通过具体的代码示例,展示了多个服务之间如何协调完成一次完整的业务流程,并保证数据的一致性。

技术本身并不难,关键在于动手去写,去验证,去调试。

坚持练习,你将很快成为分布式事务领域的“实战高手”。


🎉 你现在可以自豪地说:“我懂分布式事务了!” 🎉

如有疑问欢迎留言提问,我们一起进步!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝