分布式事务解决方案:最佳实践(零基础入门教程)

杨磊
2025-06-29 21:13
阅读 791

开篇:什么是分布式事务?它有什么用?

开篇:什么是分布式事务?它有什么用?

你有没有遇到过这样的问题:

我在做一个电商平台的订单系统,用户下单时需要扣减库存、创建订单、记录积分。这些操作分别发生在不同的服务中,如果其中某一个失败了,整个流程怎么回滚呢?

这就是分布式事务要解决的问题。

通俗理解

事务(Transaction):你可以简单理解为一组必须一起成功或一起失败的操作。比如银行转账:从 A 扣钱 → 给 B 加钱,这两个步骤必须都成功才算完成。

但在分布式系统中,这些操作可能分布在多个服务、多个数据库里,这就带来了新的挑战:如何让它们“同步”成功或失败?

这就是——分布式事务

它的核心目标是:在多个系统中保证数据一致性


环境准备:你需要安装什么?

环境准备:你需要安装什么?

我们通过一个简单的实战项目来演示分布式事务的实现。为了顺利进行开发,请先准备好以下环境:

软件清单:

  1. Java 17+
  2. Maven 3.6+
  3. MySQL 8+
  4. Spring Boot 2.7.x 或 Spring Boot 3.x
  5. IDE:IntelliJ IDEA / Eclipse(推荐 IntelliJ)
  6. Docker(可选)

开发工具安装指南:

✅ Java & Maven

  • 下载并安装 SDKMAN!,然后使用命令:
sdk install java 17.0.8-tem
sdk install maven 3.8.6

✅ MySQL

如果你是新手,可以使用 Docker 快速启动 MySQL:

docker run -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 mysql:8

或者手动下载安装 MySQL官网

✅ Spring Boot 初始化

访问:https://start.spring.io/ 选择如下依赖:

  • Spring Web
  • Spring Data JPA
  • Lombok
  • Nacos Discovery(后面会讲解)
  • Seata Starter(重要!用于分布式事务) 生成后下载并解压到本地,用 IntelliJ 打开即可。

核心概念:什么是 TCC?XA?SAGA?Seata?别急,慢慢说!

在开始写代码前,我们先了解一些最常见的分布式事务方案及其适用场景。

🧩 CAP 原则(了解一下)

  • C(一致性 Consistency)
  • A(可用性 Availability)
  • P(分区容错 Partition tolerance)

在分布式系统中,只能满足两个。所以你要根据业务需求选择合适的策略。

🌐 最常见的分布式事务解决方案有:

方案 特点 适合场景
2PC(两阶段提交) 集中式协调,效率低但一致性高 小型系统,强一致性要求
TCC(Try-Confirm-Cancel) 编程复杂,但灵活 大型电商等业务系统
SAGA(事件驱动) 易扩展,适合长周期任务 订单流程、审批流
消息队列 + 本地事务表 异步处理能力强 强调最终一致性的系统
Seata 国产开源中间件,简化分布式事务编程 微服务架构中使用最广

我们将重点学习 Seata 的 AT 模式,这是最适合初学者的分布式事务解决方案之一。


实战项目:基于 Seata 实现一个商品下单案例

现在我们一起来动手完成一个完整的项目:用户下单 → 库存减少 → 积分增加

🔍 项目结构概览

我们将构建以下三个微服务:

  1. order-service:处理订单生成
  2. inventory-service:负责库存管理
  3. points-service:管理用户积分
  4. seata-server:Seata 服务器(协调分布式事务)

第一步:初始化 Seata Server(分布式事务协调中心)

下载地址:

前往 Seata GitHub Releases 下载最新的 seata-server-bundle-xxx.zip

解压并运行:

unzip seata-server-bundle-1.6.1.zip
cd seata/bin
sh seata-server.sh -p 8091 -n 1 -h 127.0.0.1

运行后你会看到输出日志中有类似 “Server started successfully” 的提示。


第二步:配置数据库与事务组

创建事务表(每张业务表都需要一张 undo_log 表,由 Seata 自动回滚使用)

CREATE DATABASE order_db;
USE order_db;

-- 创建 undo_log 表(每个微服务的数据库都要加这张表)
CREATE TABLE `undo_log` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `branch_id` BIGINT(20) NOT NULL,
  `xid` VARCHAR(100) NOT NULL,
  `context` VARCHAR(128) NOT NULL,
  `rollback_info` LONGBLOB NOT NULL,
  `log_status` INT(11) NOT NULL,
  `log_created` DATETIME NOT NULL,
  `log_modified` DATETIME NOT NULL,
  `ext` VARCHAR(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

-- 同理,在 inventory_db 和 points_db 中也创建此表

第三步:创建第一个服务 order-service

添加 Seata 依赖(pom.xml

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

修改 application.yml

server:
  port: 8081

spring:
  application:
    name: order-service
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/order_db?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456

seata:
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
  tx-service-group: my_tx_group

写一个下单接口(OrderController.java)

@RestController
public class OrderController {

    @Autowired
    private OrderService orderService;

    @GetMapping("/create")
    public String createOrder() {
        try {
            orderService.createOrder();
            return "订单创建成功";
        } catch (Exception e) {
            return "订单创建失败:" + e.getMessage();
        }
    }
}

对应的 service(OrderService.java)

@Service
@RequiredArgsConstructor
public class OrderService {

    private final InventoryClient inventoryClient;
    private final PointsClient pointsClient;

    @GlobalTransactional // Seata 控制的分布式事务注解
    public void createOrder() {
        System.out.println("正在创建订单...");
        // 调用其他服务模拟下单
        inventoryClient.deductInventory();
        pointsClient.addPoints();
        
        if (true) { // 强制抛出异常,触发回滚
            throw new RuntimeException("测试回滚");
        }
    }
}

第四步:创建 inventory-service 和 points-service

这两部分与 order-service 结构相似,只是分别负责不同的业务:

示例:inventory-service 的关键代码(InventoryService.java)

@Service
public class InventoryService {

    @Autowired
    private InventoryRepository inventoryRepository;

    @Transactional // 注意这里不需要加 GlobalTransactional
    public void deductInventory() {
        // 模拟库存扣减
        System.out.println("正在扣减库存...");
        inventoryRepository.decreaseStockById(1L);
    }
}

示例:points-service 的关键代码(PointsService.java)

@Service
public class PointsService {

    @Transactional
    public void addPoints() {
        System.out.println("正在增加积分...");
        // 这里可以执行数据库更新操作
    }
}

第五步:运行项目,观察效果

  1. 启动 Seata Server。
  2. 启动 Nacos 注册中心(可以先使用默认配置)。
  3. 启动三个微服务:order、inventory、points。
  4. 访问 http://localhost:8081/create

预期输出:

订单创建失败:java.lang.RuntimeException: 测试回滚

而此时,库存不会被真正扣减、积分也不会增加 —— 分布式事务已经生效!


常见问题解答

❓1. 报错:Branch session not found, may be timeout?

这是 Seata 默认事务超时导致的。可以在配置中延长超时时间:

seata:
  client:
    async-commit-buffer-limit: 10000
    lock:
      retry-interval: 10
    transaction:
      commit-retry-count: 5
      rollback-retry-count: 5

❓2. 使用 AT 模式为什么一定要建 undo_log 表?

AT 模式基于数据库快照自动帮你做回滚操作,而这个快照信息就存在 undo_log 表里。

不加这张表会导致事务无法正常回滚。


学习建议:下一步学什么?

恭喜你完成了第一个分布式事务项目的搭建和运行!

接下来你可以沿着以下路线深入学习:

🚀 推荐进阶学习路径:

  1. 掌握 Seata 的不同模式(AT/TCC/SAGA/XA)
  2. 理解微服务注册中心 Nacos/Eureka/Consul 的作用
  3. 研究 RocketMQ + 本地事务表 的异步方式
  4. 结合 DDD 设计更复杂的业务模型
  5. 探索 Dubbo 与 Spring Cloud Alibaba 的整合
  6. 阅读 Seata 源码,提升底层原理认知

📚 推荐资料:

  • Seata官方文档:https://seata.io/zh-cn/
  • 《Spring微服务实战》书籍
  • 慕课网、B站搜索 “Seata 入门”、“分布式事务详解”

总结

本文从零到一,带你了解并实践了一个分布式事务的实际项目。通过使用 Seata 的 AT 模式,我们实现了跨服务的数据一致性控制,并对事务的基本概念有了初步认识。

虽然目前只是一个简单的示例,但它为你打开了通往微服务领域的大门。

保持学习,继续深入,未来的你一定会感谢今天努力的自己!


文末小彩蛋:你可以把本项目上传到 GitHub,并尝试添加更多功能,如支付模块、订单状态流转、重试机制等。欢迎留言交流学习心得~

评论 0

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