分布式事务怎么搞?Spring Boot实战入门指南
大家好,我是掘金上常写教程的全栈工程师。最近在带实习生时,发现很多同学一听到“分布式事务”就发怵,觉得是高不可攀的“大厂专属技术”。其实不然!我当初学的时候也是一头雾水,但只要拆解清楚、动手实践,你会发现它没那么神秘。今天这篇教程,就是专门写给零基础的同学——哪怕你刚学完 Spring Boot 的 Hello World,也能跟着一步步搞懂分布式事务的核心思路和最佳实践。
一、什么是分布式事务?为什么需要它?
想象一下:你在网上买了一本书,系统要同时做两件事:
- 扣减库存(商品服务)
- 生成订单(订单服务)
这两个操作分别在两个不同的微服务中完成。如果第一步成功了,第二步失败了,就会出现“库存少了但没下单”的尴尬局面——数据不一致!
分布式事务,就是要保证跨多个服务/数据库的操作,要么全部成功,要么全部回滚,就像一个原子操作。
💡 简单说:本地事务管一个数据库,分布式事务管多个数据库/服务。
二、开发环境准备(5分钟搞定)
我们用最主流的组合:Spring Boot + MySQL。虽然标题提到了 Python,但分布式事务在 Java 生态(尤其是 Spring Cloud)中更成熟,Python 多用于辅助测试或脚本。我会在文末说明 Python 的角色。
所需工具清单:
| 工具 | 版本建议 | 用途 |
|---|---|---|
| JDK | 17 | Java 运行环境 |
| Maven | 3.8+ | 项目依赖管理 |
| MySQL | 8.0 | 数据库 |
| IntelliJ IDEA | 最新版 | 开发 IDE |
| Python | 3.8+(可选) | 写测试脚本 |
创建两个 Spring Boot 项目
# 订单服务
spring init --dependencies=web,data-jpa,mysql order-service
# 库存服务
spring init --dependencies=web,data-jpa,mysql inventory-service
每个项目的 application.yml 配置如下(记得改数据库名):
# order-service/src/main/resources/application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/order_db?useSSL=false&serverTimezone=UTC
username: root
password: your_password
jpa:
hibernate:
ddl-auto: update
server:
port: 8081
库存服务同理,端口改为 8082,数据库名为 inventory_db。
三、核心概念:三大主流方案对比
分布式事务没有银弹,但有几种经典模式。新手先掌握这三种就够了:
| 方案 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 2PC(两阶段提交) | 协调者统一指挥 | 强一致性 | 性能差、阻塞 | 传统金融系统 |
| TCC(Try-Confirm-Cancel) | 业务层面补偿 | 灵活、高性能 | 代码复杂 | 电商、支付 |
| Saga 模式 | 事件驱动 + 补偿 | 易理解、适合长事务 | 最终一致性 | 订单履约、物流 |
🚫 避坑提醒:初学者别一上来就啃 2PC!它对数据库有强依赖,且性能瓶颈明显。TCC 和 Saga 更适合现代微服务架构。
四、实战:用 TCC 模式实现下单+扣库存
我们用 TCC 模式来实现“创建订单 + 扣减库存”。关键思想是:先预留资源,再确认或取消。
步骤 1:定义库存服务的 TCC 接口
在 inventory-service 中:
// InventoryController.java
@RestController
public class InventoryController {
@PostMapping("/inventory/reserve")
public ResponseEntity<String> reserve(@RequestParam Long productId, @RequestParam Integer quantity) {
// Try 阶段:冻结库存(比如 total=100, frozen=10)
inventoryService.freezeStock(productId, quantity);
return ResponseEntity.ok("reserved");
}
@PostMapping("/inventory/confirm")
public ResponseEntity<String> confirm(@RequestParam Long productId, @RequestParam Integer quantity) {
// Confirm 阶段:真正扣减库存
inventoryAssistant.confirmStock(productId, quantity);
return ResponseEntity.ok("confirmed");
}
@PostMapping("/inventory/cancel")
public ResponseEntity<String> cancel(@RequestParam Long productId, @RequestParam Integer quantity) {
// Cancel 阶段:释放冻结库存
inventoryService.releaseFrozenStock(productId, quantity);
return ResponseEntity.ok("cancelled");
}
}
步骤 2:在订单服务中编排 TCC 流程
在 order-service 中,我们模拟一个下单流程:
@Service
public class OrderService {
private final RestTemplate restTemplate = new RestTemplate();
public String createOrder(Long productId, Integer quantity) {
try {
// 1. Try: 预留库存
restTemplate.postForObject(
"http://localhost:8082/inventory/reserve?productId=" + productId + "&quantity=" + quantity,
null, String.class
);
// 2. 本地创建订单(假设这里可能抛异常)
Order order = new Order(productId, quantity);
orderRepository.save(order);
// 3. Confirm: 确认扣库存
restTemplate.postForObject(
"http://localhost:8082/inventory/confirm?productId=" + productId + "&quantity=" + quantity,
null, String.class
);
return "Order created successfully";
} catch (Exception e) {
// 4. 出错?Cancel!
restTemplate.postForObject(
"http://localhost:8082/inventory/cancel?productId=" + productId + "&quantity=" + quantity,
null, String.class
);
throw new RuntimeException("Order failed, rolled back", e);
}
}
}
✅ 这就是最简版 TCC!虽然没有用 Seata 等框架,但逻辑清晰,适合理解原理。
五、新手常见问题解答
Q1:为什么不用数据库事务直接解决?
A:因为订单和库存是两个数据库,本地事务无法跨库。MySQL 的 XA 事务(2PC)性能太差,不适合高并发场景。
Q2:TCC 写起来好麻烦,有没有简化方案?
A:有!推荐使用 Seata 框架。它通过注解自动处理 TCC 流程,比如:
@GlobalTransactional
public void createOrder() { ... }
但建议先手写一遍,再学框架,否则容易“知其然不知其所以然”。
Q3:Python 能用来做什么?
A:虽然核心服务用 Java,但你可以用 Python 写自动化测试脚本,模拟高并发下单,验证事务是否可靠:
# test_distributed_tx.py
import requests
import threading
def place_order():
try:
resp = requests.post("http://localhost:8081/order/create?productId=1&quantity=1")
print(f"Result: {resp.text}")
except Exception as e:
print(f"Error: {e}")
# 模拟10个并发用户
threads = [threading.Thread(target=place_order) for _ in range(10)]
for t in threads: t.start()
for t in threads: t.join()
六、面试题高频考点
面试官最爱问这些:
“你们系统如何保证分布式事务一致性?”
→ 回答模板:“我们采用 TCC 模式,通过 Try 预留资源、Confirm 提交、Cancel 补偿,配合 Seata 框架实现。”“TCC 和 Saga 有什么区别?”
→ 关键点:TCC 是同步的(强隔离),Saga 是异步事件链(最终一致,需处理空补偿、幂等)。“如何保证 Cancel 操作一定能执行?”
→ 引入本地消息表或定时对账任务,确保补偿不丢失。
七、下一步学习建议
- 深入框架:学习 Seata 官方文档,尝试用
@GlobalTransactional替代手写 TCC。 - 理解 CAP:分布式事务本质是 CAP 理论的实践——通常选择 AP(可用性+分区容错),牺牲强一致性。
- 动手扩展:给你的 demo 加上 Redis 缓存、RabbitMQ 异步解耦,体验更复杂的场景。
- 读源码:Seata 的 AT 模式源码很清晰,适合进阶。
结语
分布式事务不是魔法,而是一套设计思维:当系统拆分后,如何用工程手段兜底数据一致性。我当初也是从手写 TCC 开始,慢慢才理解 Seata、RocketMQ 事务消息等高级方案。记住:先跑通,再优化;先理解,再封装。
如果你跟着这篇教程跑通了代码,恭喜你——已经超过了 80% 只看理论不动手的人!接下来,去 GitHub 找个开源项目贡献吧,实战才是成长最快的方式。
🌟 最后彩蛋:关注我,下期教你《用 Python + FastAPI 快速搭建分布式事务的 Mock 服务》,让 Java 后端调试更轻松!

评论 0