分布式事务解决方案:最佳实践(零基础入门教程)
开篇:什么是分布式事务?它有什么用?

你有没有遇到过这样的问题:
我在做一个电商平台的订单系统,用户下单时需要扣减库存、创建订单、记录积分。这些操作分别发生在不同的服务中,如果其中某一个失败了,整个流程怎么回滚呢?
这就是分布式事务要解决的问题。
通俗理解
事务(Transaction):你可以简单理解为一组必须一起成功或一起失败的操作。比如银行转账:从 A 扣钱 → 给 B 加钱,这两个步骤必须都成功才算完成。
但在分布式系统中,这些操作可能分布在多个服务、多个数据库里,这就带来了新的挑战:如何让它们“同步”成功或失败?
这就是——分布式事务。
它的核心目标是:在多个系统中保证数据一致性。
环境准备:你需要安装什么?

我们通过一个简单的实战项目来演示分布式事务的实现。为了顺利进行开发,请先准备好以下环境:
软件清单:
- Java 17+
- Maven 3.6+
- MySQL 8+
- Spring Boot 2.7.x 或 Spring Boot 3.x
- IDE:IntelliJ IDEA / Eclipse(推荐 IntelliJ)
- 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 实现一个商品下单案例
现在我们一起来动手完成一个完整的项目:用户下单 → 库存减少 → 积分增加
🔍 项目结构概览
我们将构建以下三个微服务:
order-service:处理订单生成inventory-service:负责库存管理points-service:管理用户积分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("正在增加积分...");
// 这里可以执行数据库更新操作
}
}
第五步:运行项目,观察效果
- 启动 Seata Server。
- 启动 Nacos 注册中心(可以先使用默认配置)。
- 启动三个微服务:order、inventory、points。
- 访问 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 表里。
不加这张表会导致事务无法正常回滚。
学习建议:下一步学什么?
恭喜你完成了第一个分布式事务项目的搭建和运行!
接下来你可以沿着以下路线深入学习:
🚀 推荐进阶学习路径:
- 掌握 Seata 的不同模式(AT/TCC/SAGA/XA)
- 理解微服务注册中心 Nacos/Eureka/Consul 的作用
- 研究 RocketMQ + 本地事务表 的异步方式
- 结合 DDD 设计更复杂的业务模型
- 探索 Dubbo 与 Spring Cloud Alibaba 的整合
- 阅读 Seata 源码,提升底层原理认知
📚 推荐资料:
- Seata官方文档:https://seata.io/zh-cn/
- 《Spring微服务实战》书籍
- 慕课网、B站搜索 “Seata 入门”、“分布式事务详解”
总结
本文从零到一,带你了解并实践了一个分布式事务的实际项目。通过使用 Seata 的 AT 模式,我们实现了跨服务的数据一致性控制,并对事务的基本概念有了初步认识。
虽然目前只是一个简单的示例,但它为你打开了通往微服务领域的大门。
保持学习,继续深入,未来的你一定会感谢今天努力的自己!
文末小彩蛋:你可以把本项目上传到 GitHub,并尝试添加更多功能,如支付模块、订单状态流转、重试机制等。欢迎留言交流学习心得~

评论 0