分布式事务解决方案:最佳实践(面向零基础初学者)
一、开篇:什么是分布式事务?它用来做什么?

在开发大型系统时,我们常常会遇到一个业务操作需要跨多个服务或数据库的情况。比如你在网上下单买了一件商品,这个操作背后可能会涉及:
- 库存服务:减少库存数量
- 订单服务:创建订单记录
- 支付服务:完成支付操作
这三个服务通常是独立部署的,它们的数据也分别存储在不同的数据库中。那么问题来了:如果其中某一个步骤失败了,比如支付失败,但订单已经创建成功了,怎么办呢?
这就是分布式事务要解决的问题——确保所有相关的操作要么全部成功,要么全部失败,保持数据一致性。
听起来很复杂?别担心,这篇文章就是写给完全没接触过这个概念的同学看的。我们从环境搭建开始,一步步带你做个小项目,让你真正理解分布式事务,并写出自己的代码。
二、环境准备:搭建我们的学习环境

为了学习分布式事务,我们需要以下几个工具和环境:
1. 安装 JDK
- 推荐使用 JDK 8 或 11
- 官网下载地址:https://www.oracle.com/java/technologies/javase-downloads.html
2. 安装 IntelliJ IDEA(IDE)
- 下载社区版免费使用:https://www.jetbrains.com/idea/download/
3. 安装 MySQL 数据库
- 下载地址:https://dev.mysql.com/downloads/installer/
- 可选安装工具:Navicat 管理数据库更方便
4. 安装 Nacos(用于服务注册与发现)
- 下载地址:https://github.com/alibaba/nacos/releases
- 启动命令(Windows):
startup.cmd -m standalone - 启动后访问
http://localhost:8848/nacos登录默认账号:nacos/nacos
5. 安装 Seata(我们今天用到的分布式事务框架)
- 下载地址:https://github.com/seata/seata/releases
- 配置 Seata 数据库(需要创建三个库对应不同角色)
- 启动命令(Windows):
seata-server.bat -p 8091 -h 127.0.0.1 -m db
✅ 搞定以上步骤后,就可以开始动手写代码啦!
三、核心概念:通俗易懂地讲清楚关键点

1. 本地事务 vs 分布式事务
| 类型 | 特点 | 示例 |
|---|---|---|
| 本地事务 | 一个数据库中的事务 | 转账:A扣钱 + B加钱 |
| 分布式事务 | 多个数据库或服务之间的事务协作 | 创建订单 + 扣库存 + 支付 |
🧠 小知识:本地事务有四大特性 ACID(原子性、一致性、隔离性、持久性),而分布式事务的核心目标是实现类似的功能,但在多个服务间。
2. CAP 理论(一定要知道)
CAP 是分布式系统的三大核心要求:
- C:一致性(Consistency)
- A:可用性(Availability)
- P:分区容忍性(Partition Tolerance)
⚠️ 在网络出现故障时,最多只能同时满足两个:通常牺牲一致性或可用性。
📌 学习建议:理解 CAP 帮助你在设计系统时做出权衡。
3. 常见的分布式事务方案对比
| 方案名 | 是否强一致性 | 是否适合高并发 | 实现难度 | 场景推荐 |
|---|---|---|---|---|
| 两阶段提交 | 是 | 否 | 中等 | 小规模系统、银行转账 |
| TCC | 是 | 是 | 高 | 电商交易、金融场景 |
| Saga模式 | 否 | 是 | 中等 | 长流程、可补偿的场景 |
| 消息队列+本地事务 | 否 | 是 | 中等 | 异步处理、日志记录等 |
| Seata(AT模式) | 是 | 中等 | 低 | Spring Boot 项目推荐使用 |
✅ 我们这次选择 Seata 的 AT 模式,因为它对开发者友好,集成简单,适合刚入门的同学。
四、实战项目:从零开始搭建一个简单的分布式事务系统

我们现在要实现一个“下订单减库存”的功能,包括两个服务:
- OrderService(订单服务)
- InventoryService(库存服务)
我们要确保:下单成功时库存减少,否则两者都不变。
第一步:创建 Spring Boot 项目
用 IntelliJ IDEA 新建两个模块:
- order-service
- inventory-service
都添加如下依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2022.0.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
第二步:配置数据库
每个服务连接各自的数据库:
OrderService 数据库结构:
CREATE DATABASE order_db;
USE order_db;
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
product_id BIGINT NOT NULL,
count INT NOT NULL
);
InventoryService 数据库结构:
CREATE DATABASE inventory_db;
USE inventory_db;
CREATE TABLE inventory (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
product_id BIGINT NOT NULL UNIQUE,
stock INT NOT NULL
);
第三步:配置 Seata(以 OrderService 为例)
在 resources 下创建 file.conf 和 registry.conf 文件:
file.conf(简化示例):
transport.type="TCP"
store.mode="db"
store.db.datasource="druid"
store.db.url="jdbc:mysql://127.0.0.1:3306/seata"
store.db.user="root"
store.db.password="your_password"
registry.conf(使用 Nacos):
registry.type = "nacos"
registry.nacos.server-addr = "127.0.0.1:8848"
第四步:编写核心业务代码
OrderController.java(订单服务)
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/create")
public String createOrder() {
try {
orderService.createOrder();
return "下单成功";
} catch (Exception e) {
return "下单失败:" + e.getMessage();
}
}
}
OrderServiceImpl.java(订单服务逻辑)
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private RestTemplate restTemplate;
@GlobalTransactional // 开启全局事务
@Override
public void createOrder() {
// 1. 创建订单
System.out.println("创建订单...");
// 2. 调用库存服务减库存(假设调用失败将回滚)
ResponseEntity<String> response = restTemplate.getForEntity(
"http://inventory-service/inventory/reduce", String.class);
if (!response.getStatusCode().is2xxSuccessful()) {
throw new RuntimeException("库存扣减失败");
}
// 如果到这里都没异常,就提交事务
}
}
InventoryController.java(库存服务)
@RestController
@RequestMapping("/inventory")
public class InventoryController {
@PostMapping("/reduce")
public String reduceStock() {
System.out.println("库存扣减...");
// 实际业务逻辑更新数据库
// 测试用,返回失败模拟异常
// return "fail";
return "success";
}
}
第五步:测试一下!
- 启动 Nacos
- 启动 Seata Server
- 启动 order-service 和 inventory-service
- 打开浏览器访问:
http://localhost:8080/order/create
🎉 如果一切正常,你会看到下单和减库存同步成功;如果库存服务返回 fail,则整个事务都会回滚!
五、常见问题解答(FAQ)
Q1:为什么我启动项目报错说找不到 datasource?
- 检查你的
application.yml中是否正确配置了数据源 - 确保
url、用户名、密码正确无误
Q2:Seata 怎么和微服务注册中心对接?
- 修改
registry.conf文件,把type设置为nacos,并填好 Nacos 地址即可
Q3:@GlobalTransactional 注解失效怎么办?
- 检查是否有异常抛出,没有抛出不会触发回滚
- 确保调用方使用了
RestTemplate或 Feign 并做了拦截配置
Q4:我可以用别的语言或框架吗?
- Seata 支持 Java、Go、Python 等多种语言,也可以和 Dubbo 集成
- 本教程基于 Spring Cloud + Seata,适合 Java 初学者入门
六、学习建议:下一步怎么学?
恭喜你完成了第一个分布式事务小项目!接下来你可以沿着以下路径继续学习:
1. 学习更多分布式事务框架
- 掌握 TCC(Try-Confirm-Cancel)模型
- 了解消息队列结合本地事务的实现方式(如 RocketMQ)
- 尝试 Apache ServiceComb Saga 或 Atomikos
2. 深入理解 Seata 的底层机制
- 学习 XA 模式与 AT 模式的区别
- 了解 Seata 如何生成 undo_log 来支持事务回滚
3. 实践真实项目场景
- 实现“付款失败自动退款”
- 使用 RabbitMQ 实现异步事务
- 构建完整的电商系统架构(订单、用户、支付、物流等服务)
4. 进阶资源推荐
- 官方文档:
- GitHub 示例项目:
- 视频课程:
- B站《Spring Cloud 微服务与分布式事务》系列

结语

通过这篇教程,你已经迈出了分布式事务的第一步。虽然刚开始看起来有点难,但只要你坚持练习,不断动手写代码,就会越来越熟练。记住一句话:
“技术就像游泳,只看教程永远学不会,只有跳进水里才知道自己能不能浮起来。”
加油吧,程序员!💪
文章总字数:约3711字

评论 0