分布式事务解决方案:最佳实践(面向初学者的教程)
开篇:分布式事务是什么?为什么需要它?

在你开始接触后端开发时,可能已经了解过什么是“数据库事务”。简单来说,事务是为了确保多个操作要么都成功,要么都失败。比如转账操作——从A账户扣钱和向B账户加钱这两个动作如果其中一个失败,整个交易都应该取消。
但是,随着系统越来越复杂,数据可能分布在不同的服务器、不同的数据库中。这个时候我们就遇到了一个新问题:如何在一个服务调用另一个服务的时候,还能保证事务的一致性?
这,就是我们今天要讲的内容:分布式事务。
举个生活中的例子:
想象你在点外卖:
- 下单 → 商家接单
- 扣减用户余额
- 增加商家收入
- 修改库存数量
这些操作可能分别属于不同的服务,有的可能在支付系统中完成,有的在订单中心,有的在库存系统。如果其中某一步失败了该怎么办?你肯定不希望扣了你的钱但订单没生成,或者库存减少了但订单也没创建。
所以,我们需要一种机制来确保所有操作整体的成功或失败 —— 这就是 分布式事务 的价值所在。
环境准备:搭建我们的实验环境

🔧 目标:准备好一个支持微服务通信 + 多数据库的 Spring Boot 开发环境
使用技术栈(推荐):
- Java JDK 17+
- Spring Boot 2.7 或以上
- 数据库:
- 用户账户表(MySQL)
- 店铺收入表(PostgreSQL)
- 消息中间件(RocketMQ or RabbitMQ)
- 微服务框架(Spring Cloud Alibaba)
步骤一:安装JDK & IntelliJ IDEA(或IDEA社区版)
- 下载并安装 OpenJDK(Java 17+)
- 安装IntelliJ IDEA社区版
- 测试是否安装成功,在终端输入:
java -version
步骤二:创建父项目(Maven)
<!-- pom.xml -->
<modules>
<module>order-service</module>
<module>payment-service</module>
<module>inventory-service</module>
</modules>
每个模块对应一个微服务。
步骤三:配置数据库
用户服务(MySQL)
CREATE DATABASE user_db;
USE user_db;
CREATE TABLE `user` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(50),
`balance` DECIMAL(10,2) DEFAULT 0
);
收入服务(PostgreSQL)
CREATE DATABASE shop_db;
CREATE TABLE income(
id SERIAL PRIMARY KEY,
order_id BIGINT NOT NULL,
amount NUMERIC(10,2)
);
步骤四:集成消息队列(选型RocketMQ)
下载 RocketMQ 并启动 Broker:
unzip rocketmq-all-*.zip
cd rocketmq-*
nohup bin/mqnamesrv &
sleep 3
nohup bin/mqbroker -n localhost:9876 &
核心概念:通俗易懂解释关键术语

在学习分布式事务之前,我们要先理解几个关键词:
| 关键词 | 中文含义 | 解释说明 |
|---|---|---|
| XA 协议 | 两阶段提交协议的一种实现 | 强一致性方案,但性能差 |
| TCC | Try-Confirm-Cancel | 最流行的柔性事务模型之一 |
| SAGA | 长事务补偿机制 | 适用于业务逻辑复杂的场景 |
| 本地事务消息表 | 本地记录消息状态 | 确保本地操作和消息发送一致 |
| 事件驱动 | 发送消息驱动其他服务 | 常用于最终一致性设计 |
下面我们重点介绍最常用也最容易上手的:TCC模式
实战项目:使用 TCC 实现分布式转账


💡 项目目标:
我们模拟两个服务之间的资金转移:
- 用户服务(User Service)负责扣款
- 店铺服务(Shop Service)负责收款
- 如果其中一个失败,要回退全部操作(例如:退款、恢复库存)
第一步:添加依赖项(以 User 服务为例)
<!-- TCC 事务管理器(Seata 可选) -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
第二步:定义 TCC 接口(ShopService)
public interface ShopTccAction {
@TwoPhaseBusinessAction(name = "transferToShop")
boolean prepare(BusinessActionContext ctx);
@Commit
boolean commit(BusinessActionContext ctx);
@Rollback
boolean rollback(BusinessActionContext ctx);
}
第三步:实现接口逻辑(简化示例)
@Component
public class ShopTccActionImpl implements ShopTccAction {
@Override
public boolean prepare(BusinessActionContext ctx) {
// 准备接收金额
Long orderId = (Long) ctx.getActionContext("orderId");
Double amount = (Double) ctx.getActionContext("amount");
// 调用 DB 操作插入预收记录,但不确认到账
return true; // 表示 prepare 成功
}
@Override
public boolean commit(BusinessActionContext ctx) {
// 真正执行收入增加
Long orderId = (Long) ctx.getActionContext("orderId");
Double amount = (Double) ctx.getActionContext("amount");
// update income set status='confirmed' where orderId=...
return true;
}
@Override
public boolean rollback(BusinessActionContext ctx) {
// 回滚收入操作,可能是删除预收记录
return true;
}
}
第四步:编写主流程(UserService)
@RestController
public class UserController {
@Autowired
private UserService userService;
@Autowired
private ShopTccAction shopAction;
@GetMapping("/transfer")
public String transferMoney(@RequestParam Long userId,
@RequestParam Double amount) {
try {
BusinessActionContext ctx = new BusinessActionContext();
ctx.setActionContext("userId", userId);
ctx.setActionContext("amount", amount);
// 开始全局事务
GlobalTransactionContext.getCurrentOrCreate().begin(60000);
// 先扣款
userService.deductBalance(userId, amount); // 本地事务
// 再尝试增加商店收入
if (!shopAction.prepare(ctx)) {
throw new RuntimeException("Prepare failed");
}
// 提交所有事务
GlobalTransactionContext.getCurrent().commit();
return "转账成功";
} catch (Exception e) {
GlobalTransactionContext.getCurrent().rollback();
return "转账失败:" + e.getMessage();
}
}
}
🧪 测试一下:
访问以下地址模拟一次转账:
http://localhost:8080/transfer?userId=1&amount=100
打开数据库查看 user 表 balance 是否减少,income 表是否有记录新增。
常见问题与解答(FAQ)
✅ Q1:什么是强一致性 vs 最终一致性?
- 强一致性:所有操作必须同步完成,如数据库本地事务,保证实时一致。
- 最终一致性:允许暂时不一致,但最终会达成一致状态。适合高并发、分布式系统。
✅ Q2:TCC 和 Saga 的区别?
- TCC 是预先锁定资源,再逐步 Confirm 或 Rollback;
- Saga 是按步骤顺序执行操作,出错时反向执行补偿动作。
✅ Q3:TCC 为什么比 XA 更好?
XA 对数据库压力大、性能差,TCC 更轻量、更灵活,可以结合业务逻辑自定义提交/回滚规则。
✅ Q4:我的微服务没有 Seata 怎么做 TCC?
你可以手动实现 TCC 的 Try、Confirm、Rollback 三个方法,不需要框架也可以。
学习建议:下一步该学什么?
恭喜你完成了第一课的分布式事务实战!
接下来你可以继续深入的方向有:
📌 1. 进阶学习内容
📌 2. 项目推荐练习
- 实现一个下单+支付+库存更新的完整闭环系统
- 在 Kafka 中实现事务消息投递
📌 3. 工具推荐
- Postman:调试 REST API 接口
- Redis Desktop Manager:查看缓存
- Zipkin:跟踪分布式请求链路
- Docker + K8s:部署多个服务观察其交互过程
结语:学完之后你能做什么?
学完这篇入门教程后,你应该已经可以:
✅ 理解分布式事务的基本原理
✅ 使用 TCC 模式解决跨服务的数据一致性问题
✅ 编写带有事务保障的多服务代码
✅ 针对实际业务场景选择合适的事务解决方案
如果你是后端工程师、架构师方向的学习者,那么掌握分布式事务将是职业生涯的一个重要里程碑!
📌 附录:源码 GitHub 示例(模拟)
git clone https://github.com/example/distributed-tcc-demo.git
有问题可以在评论区留言提问哦 😊

评论 0