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

在我们开发一个网站或者应用程序时,有时候需要完成一个任务,这个任务会涉及到多个系统或服务。比如你在网上下单买了一件衣服:
- 你的购物车信息会被更新
- 商品库存要减1
- 订单系统要生成订单
- 用户的积分可能也会增加
这些操作通常是在不同的“服务”中进行的,也就是所谓的“微服务架构”。
如果这些操作中有一个失败了(比如库存减少成功但生成订单失败),就可能会导致数据混乱。比如钱扣了但没有拿到商品。
这时候,“分布式事务”就是用来解决这个问题的技术——确保所有相关系统的操作要么全部成功,要么全部不执行,保证数据的一致性。
✅ 一句话总结:分布式事务就是让多个服务一起干活,必须都干完,否则谁也别干。
环境准备:你需要装哪些东西?


我们要做一个简单的项目来演示如何实现分布式事务,所以先准备好开发环境。
所需工具(Windows/Mac/Linux通用):
| 软件 | 版本要求 | 安装说明 |
|---|---|---|
| Java JDK | ≥ 8 | 官网下载 |
| Maven | ≥ 3.6 | 官网下载 |
| MySQL | ≥ 5.7 | 安装指南 |
| Spring Boot | 不用手动安装,Maven会自动管理 | |
| IDE(推荐) | IntelliJ IDEA 或 VS Code | IDEA下载 |
步骤一:安装Java
如果你还不知道有没有安装Java:
java -version
如果没有输出版本号,那就去官网上下载安装一下。
步骤二:安装MySQL数据库
你可以使用命令行安装,或者直接下载图形化安装包(推荐新手用XAMPP一键安装包)。安装完成后设置好用户名和密码(通常是root / root或空密码)。
步骤三:验证Maven安装
打开终端输入:
mvn -v
如果有版本号,就表示安装成功。
核心概念讲解:分布式事务的关键词
在学习具体技术前,我们需要理解几个关键术语。
1. 本地事务 vs 分布式事务
| 类型 | 描述 | 示例 |
|---|---|---|
| 本地事务 | 在一个数据库内保证一致性 | 同一个数据库里的两个表操作 |
| 分布式事务 | 多个服务或数据库之间协同工作 | 库存服务 + 订单服务 + 支付服务 |
2. CAP定理(简单说)
CAP 是三个词的缩写:
- C(Consistency)一致性:所有节点看到的数据是一样的。
- A(Availability)可用性:每个请求都能得到响应。
- P(Partition Tolerance)分区容忍性:网络不稳定也能继续运行。
⚠️ 这三点不能同时满足,只能选两个重点。大多数分布式系统都选择AP(高可用+分区容忍)。
3. 常见的分布式事务方案(先记住名字,后面详细学)
| 方案名称 | 是否强一致性 | 是否复杂度低 | 场景 |
|---|---|---|---|
| 两阶段提交(2PC) | ✅ | ❌ | 少数系统间协作 |
| 三阶段提交(3PC) | ❌ | ❌ | 用于改进2PC |
| 最终一致性(TCC) | ❌ | ✅ | 高并发电商场景 |
| Saga模式 | ❌ | ✅ | 操作可逆、失败后补偿 |
| 消息队列 | ❌ | ✅ | 异步解耦、延迟容忍 |

我们这篇教程会用 TCC 模式 来做实战示例,因为它在实际中应用最广,而且适合初学者理解。
实战项目:用TCC实现一个“下单+扣库存”的例子
我们将构建两个服务:
- 订单服务(Order Service)
- 库存服务(Inventory Service)
我们要实现的功能是:
当用户下单时:
- 先检查库存是否足够
- 如果可以,预占库存(Try)
- 创建订单(Confirm)
- 减少库存(Confirm)
- 如果失败,释放库存(Cancel)
第一步:创建Spring Boot项目
使用Spring Initializr 创建两个独立项目:
Order Service
- 名称:order-service
- 添加依赖:Spring Web, MyBatis, MySQL Driver
Inventory Service
- 名称:inventory-service
- 同样添加依赖
第二步:配置MySQL数据库
创建两个数据库:
CREATE DATABASE order_db;
CREATE DATABASE inventory_db;
然后分别建立对应的表:
订单表 (order_db)
CREATE TABLE `orders` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`product_id` INT,
`quantity` INT,
`status` VARCHAR(20)
);
库存表 (inventory_db)
CREATE TABLE `inventories` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`product_id` INT UNIQUE,
`stock` INT
);
-- 插入一条初始库存
INSERT INTO inventories(product_id, stock) VALUES (1, 100);
第三步:编写业务逻辑(以TCC为例)
TCC的核心思想是三个步骤:
- Try(尝试):资源预留,不做真正更改
- Confirm(确认):确认操作,执行真实修改
- Cancel(取消):回滚操作,释放预留资源
我们在Spring Boot中使用 Seata 这个开源框架来简化TCC的实现。
🧠 Seata是一个非常流行的开源分布式事务中间件,支持TCC、AT等多种模式,我们这里只讲TCC。
第四步:引入Seata依赖(两个项目的pom.xml都要加)
在pom.xml中加入Seata的依赖:
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.6.1</version>
</dependency>
第五步:定义TCC接口
在订单服务中:
@TwoPhaseBusinessAction(name = "reduceStock")
public boolean reduceStock(@BusinessActionContextParameter(paramName = "productId") int productId,
@BusinessActionContextParameter(paramName = "quantity") int quantity);
在库存服务中:
@TwoPhaseBusinessAction(name = "tryReduceStock")
public boolean tryReduceStock(BusinessActionContext ctx);
@Commit
public boolean confirmReduceStock(BusinessActionContext ctx);
@Rollback
public boolean cancelReduceStock(BusinessActionContext ctx);
第六步:实现Try/Confirm/Cancel逻辑
Try方法(库存服务)
@Override
public boolean tryReduceStock(BusinessActionContext ctx) {
Integer productId = (Integer) ctx.getActionContext("productId");
Integer quantity = (Integer) ctx.getActionContext("quantity");
// 更新状态为预扣
return inventoryMapper.tryLock(productId, quantity);
}
Confirm方法(库存服务)
@Override
public boolean confirmReduceStock(BusinessActionContext ctx) {
Integer productId = (Integer) ctx.getActionContext("productId");
Integer quantity = (Integer) ctx.getActionContext("quantity");
// 真正减库存
return inventoryMapper.reduceStock(productId, quantity);
}
Cancel方法(库存服务)
@Override
public boolean cancelReduceStock(BusinessActionContext ctx) {
Integer productId = (Integer) ctx.getActionContext("productId");
Integer quantity = (Integer) ctx.getActionContext("quantity");
// 释放锁定
return inventoryMapper.releaseStock(productId, quantity);
}
注:这些方法中的
inventoryMapper是我们对数据库的MyBatis封装类。
第七步:订单服务调用TCC接口
在订单服务中,我们通过Feign调用库存服务的Try接口:
@GlobalTransactional
public void placeOrder(int productId, int quantity) {
inventoryService.reduceStock(productId, quantity); // Try操作
// 创建订单
Order order = new Order();
order.setProductId(productId);
order.setQuantity(quantity);
order.setStatus("CREATED");
orderMapper.insert(order);
// 如果执行到这里还没异常,就会自动触发confirm
}
第八步:测试流程
正常流程:
- 下单成功 → 预扣库存 → 创建订单 → 减库存
异常流程(例如插入订单时报错):
- 预扣库存 → 抛出异常 → 自动触发Cancel → 释放库存
新手常见问题解答
Q1:为什么不用简单的数据库事务?
因为普通的数据库事务只能作用在一个数据库实例上,而分布式系统是多个服务、多个数据库,无法直接用传统事务控制。
Q2:TCC一定要有Confirm和Cancel吗?
是的。这是TCC的基础,缺少任何一个部分都无法保证最终一致性。
Q3:代码中出现"Branch Rollbacked"是什么意思?
这说明某个分支服务的Cancel操作被触发了,通常是发生了异常导致全局事务回滚。
Q4:Seata必须要部署吗?
是的。Seata Server作为协调者,负责管理事务的生命周期,必须单独启动。
学习建议:下一步该怎么走?
恭喜你完成了第一个分布式事务的实战项目!
接下来你可以沿着这几个方向继续深入:
推荐路线图:
进阶阅读:
- 学习其他分布式事务模型(如Saga、2PC、AT)
- 理解CAP与BASE理论的区别
- 阅读Seata官方文档:Seata Docs
扩展技能树:
- 学习消息队列(如RocketMQ、Kafka)用于异步通信
- 学习服务注册发现(Nacos、Eureka)
- 学习链路追踪(SkyWalking、Zipkin)
实战演练平台:
- GitHub 上搜索 “distributed transaction example”
- 使用云厂商(阿里云、腾讯云)提供的分布式事务服务试用
结语:你已经入门了!
虽然分布式事务听起来很深奥,但它其实就是为了确保多个服务之间的操作能“步调一致”。
只要掌握了核心理念,再加上一些框架(比如Seata),你也能写出可靠的分布式系统!
🍀 学编程就像打怪升级,现在你已经击败了“分布式事务BOSS”,继续加油吧!
文章字数统计:约3697字
如果你觉得这篇文章对你有帮助,请点赞或收藏!欢迎留言提问,我会尽力解答。

评论 0