分布式事务解决方案:最佳实践(面向零基础初学者)
一、开篇:分布式事务是啥?为啥重要?

你可能听说过“事务”这个词,比如在银行转账时,如果A给B转账100元,必须保证两件事同时完成:
- A账户减100元
- B账户加100元
如果其中一步出错(比如系统崩溃),那整个操作都要撤销,否则就会出现数据错误,这就是本地事务。
但在现代软件开发中,特别是大型互联网系统里,一个业务往往要调用多个服务。比如电商平台下单可能涉及以下服务:
- 订单服务(生成订单)
- 库存服务(扣除库存)
- 支付服务(扣款)
这些服务通常部署在不同的服务器上,甚至可能是不同的技术栈,这种情况下的事务就叫做分布式事务。
✅ 分布式事务的目标
确保多个服务参与的业务操作要么全部成功,要么全部失败。
听起来很理想,但实现起来却不容易。所以接下来,我们就来一步步学习如何解决这个问题!
二、环境准备:你需要哪些工具?

为了能跟着教程动手操作,你需要先准备好下面这些环境:
🖥️ 基本要求
- 操作系统:Windows / macOS / Linux 都可以
- Java 环境:JDK 8 或更高版本
- IDE:IntelliJ IDEA 社区版 或 Eclipse
- 构建工具:Maven 或 Gradle
- 数据库:MySQL 或 MariaDB(如果你不熟悉数据库,可以暂时使用 H2 内存数据库)
- 消息中间件(可选):RabbitMQ 或 RocketMQ(用于最终一致性方案)
⚙️ 安装步骤(简要)
小贴士:如果你不想折腾环境,也可以直接使用 Spring Initializr 搭建项目结构,然后本地运行。
三、核心概念:轻松理解关键术语
在真正写代码之前,我们先简单了解几个关键词:
🔁 1. 本地事务 vs 分布式事务
| 类型 | 适用场景 | 特点 |
|---|---|---|
| 本地事务 | 单个数据库操作 | 简单,ACID 可保障 |
| 分布式事务 | 多个微服务协作处理业务 | 复杂,需要特殊方案 |
🧩 2. CAP 理论(通俗解释)
CAP 是分布式系统的三大特性:
- C(一致性):所有节点看到的数据一致
- A(可用性):每个请求都能得到响应
- P(分区容忍):网络出问题也得正常工作
⚠️ 在分布式系统中,你只能三选二!比如多数系统选择 AP(高可用+分区容忍),但会牺牲强一致性。
🌐 3. 最常见的分布式事务解决方案(我们重点讲这些)
| 方案名称 | 原理说明 | 优点 | 缺点 |
|---|---|---|---|
| 2PC(两阶段提交) | 协调者统一协调各参与者事务 | 强一致性 | 性能差,有单点故障风险 |
| TCC(Try-Confirm-Cancel) | 拆分事务为三个阶段,各自补偿 | 高性能,适合复杂交易 | 实现复杂 |
| Saga 模式 | 每个步骤都有反向操作,逐步执行 | 易实现,适合长流程 | 一致性延迟 |
| 最终一致性(基于消息) | 利用消息队列异步通知 | 高并发,低耦合 | 数据短暂不一致 |
接下来我们会从最简单的入手,教你一步步实现其中一个方案。
四、实战项目:TCC 模式实现库存扣减+支付
我们以“下单”这个功能为例,演示一个典型的分布式事务场景。
💡 场景描述
用户下订单时:
- 库存服务减少库存数量
- 支付服务扣除用户余额
- 订单服务创建订单记录
我们采用 TCC 模式 来实现这个需求,因为它比较常见,而且性能较好。
🪄 TCC 的三个核心阶段
- Try(尝试阶段):检查资源是否充足,并做预占(例如冻结资金、预留库存)
- Confirm(确认阶段):执行正式操作(如扣减库存、扣款)
- Cancel(回滚阶段):如果某个步骤失败,就释放 Try 阶段占用的资源
🧱 步骤 1:搭建基本项目结构
我们假设你已经通过 Spring Initializr 创建了一个 Spring Boot 项目,包含如下模块:
order-service/
inventory-service/
payment-service/
每个服务都可以单独运行,并暴露 REST API。
📦 步骤 2:设计接口和服务逻辑
(1)库存服务 InventoryService
@RestController
@RequestMapping("/inventory")
public class InventoryController {
@PostMapping("/try")
public String tryReduceStock(@RequestParam String productId, @RequestParam int count) {
// 检查是否足够库存,并预占库存(比如设置为锁定状态)
System.out.println("Try - 锁定 " + count + " 个商品: " + productId);
return "SUCCESS";
}
@PostMapping("/confirm")
public String confirmReduceStock(@RequestParam String productId, @RequestParam int count) {
// 正式减少库存
System.out.println("Confirm - 减少 " + count + " 个商品: " + productId);
return "SUCCESS";
}
@PostMapping("/cancel")
public String cancelLockStock(@RequestParam String productId, @RequestParam int count) {
// 释放锁定库存
System.out.println("Cancel - 释放 " + count + " 个商品: " + productId);
return "SUCCESS";
}
}
(2)支付服务 PaymentService
@RestController
@RequestMapping("/payment")
public class PaymentController {
@PostMapping("/try")
public String tryDeductPayment(@RequestParam String userId, @RequestParam double amount) {
System.out.println("Try - 冻结金额: " + amount + " 元");
return "SUCCESS";
}
@PostMapping("/confirm")
public String confirmDeductPayment(@RequestParam String userId, @RequestParam double amount) {
System.out.println("Confirm - 扣除金额: " + amount + " 元");
return "SUCCESS";
}
@PostMapping("/cancel")
public String cancelDeductPayment(@RequestParam String userId, @RequestParam double amount) {
System.out.println("Cancel - 解冻金额: " + amount + " 元");
return "SUCCESS";
}
}
🧪 步骤 3:订单服务整合 TCC 调用逻辑
@Service
public class OrderService {
private final RestTemplate restTemplate = new RestTemplate();
public String placeOrder(String userId, String productId, int productCount, double price) {
String inventoryBaseUrl = "http://localhost:8082/inventory/";
String paymentBaseUrl = "http://localhost:8081/payment/";
try {
// 1. Try 阶段
restTemplate.postForObject(inventoryBaseUrl + "try?productId=" + productId + "&count=" + productCount, null, String.class);
restTemplate.postForObject(paymentBaseUrl + "try?userId=" + userId + "&amount=" + price, null, String.class);
// 2. Confirm 阶段
restTemplate.postForObject(inventoryBaseUrl + "confirm?productId=" + productId + "&count=" + productCount, null, String.class);
restTemplate.postForObject(paymentBaseUrl + "confirm?userId=" + userId + "&amount=" + price, null, String.class);
return "订单创建成功";
} catch (Exception e) {
// 3. Cancel 阶段(任意失败都回滚)
restTemplate.postForObject(inventoryBaseUrl + "cancel?productId=" + productId + "&count=" + productCount, null, String.class);
restTemplate.postForObject(paymentBaseUrl + "cancel?userId=" + userId + "&amount=" + price, null, String.class);
return "订单创建失败,已回滚";
}
}
}
⚠️ 注意:
- 上面用了
RestTemplate直接调用其他服务,实际生产环境中建议使用 Feign 或 Dubbo 这样的远程调用框架。 - 没有引入事务管理器或日志记录,仅作演示使用。
五、常见问题:新手容易遇到的问题和解答
❓ 问题 1:什么是“幂等性”?为什么重要?
幂等性意思是:无论同一个请求被调用多少次,结果是一样的。比如支付确认操作调用一次或十次,只应该生效一次。
在分布式事务中,因为网络不稳定,可能会重试多次调用,如果没有幂等处理,可能会重复扣款或库存扣减。
解决方法:
- 使用唯一标识符(如 orderId)进行去重判断
- 在数据库中记录是否已经执行过 Confirm / Cancel
❓ 问题 2:为什么不用数据库事务来解决?
因为在微服务架构下,不同服务连接的是不同的数据库,而数据库之间的事务不能共享,也就无法保证多库之间的一致性。
❓ 问题 3:TCC 和 2PC 有什么区别?
| 比较项 | TCC | 2PC |
|---|---|---|
| 是否阻塞 | 否 | 是 |
| 适用场景 | 高性能,长期业务 | 短期业务,系统内事务控制 |
| 实现复杂度 | 较高 | 简单 |
| 成本与维护 | 更大 | 更小 |
六、学习建议:下一步你可以学什么?
恭喜你完成了第一个分布式事务项目的实战!现在你可以继续深入学习:
📘 推荐进阶方向
- 深入学习 Seata 框架
- 官网:https://seata.io
- 提供了对 AT、TCC、SAGA 等多种模式的支持
- 掌握消息队列中的事务机制
- 学习 RocketMQ、Kafka 的事务性消息
- 探索 Saga 模式(适用于流程更长的业务)
- 结合微服务治理工具学习(如 Sentinel、Nacos)
- 阅读阿里巴巴、京东等大厂的技术博客,了解真实案例
✅ 总结

本文通过一个完整的实战案例,带你从零开始了解并实现了 TCC 模式的分布式事务解决方案。虽然只是入门级内容,但为你打下了良好的基础。
记住一句话:分布式事务没有银弹,只有合适与否。关键是根据你的业务特点选择合适的策略。
希望这篇教程对你来说实用、有趣又清晰。继续努力吧,成为分布式架构高手的路上你才刚刚起步!
🚀 如有任何疑问,欢迎留言讨论~

评论 0