分布式事务解决方案:最佳实践
开篇:分布式事务是用来做什么的?

在软件开发的世界里,事务(Transaction)就像是一个“要么全做,要么全不做”的操作包。举个简单的例子:你在网上下单买东西,系统会扣掉你的余额,同时库存减少。这两步操作必须同时成功或同时失败,否则就容易出现数据不一致的问题。
那什么是分布式事务呢?想象这样一个场景:你在淘宝下单,订单服务负责创建订单,支付服务负责扣钱,库存服务负责减库存。这三个服务各自运行在不同的服务器上,甚至用的是不同的数据库。这种情况下,如何保证三个操作的一致性?这就需要使用分布式事务了。
简单来说,分布式事务就是在多个服务或系统之间协调一组操作,确保它们要么全部完成,要么全部撤销。
环境准备:搭建我们的开发环境

为了更好地学习和实践,我们需要先准备好基础开发环境。下面我们将一步步搭建:
1. 安装 JDK(Java Development Kit)
- 推荐版本:JDK 17
- 下载地址:https://adoptium.net/
- 安装后在终端输入命令确认是否安装成功:
java -version
2. 安装 Spring Boot 开发工具(IntelliJ IDEA 或 VS Code + Spring Boot 插件)
推荐使用 IntelliJ IDEA 社区版:下载页面
3. 安装 MySQL 数据库(用于模拟不同服务的数据存储)
- 下载地址:https://dev.mysql.com/downloads/installer/
- 安装完成后,用 Navicat 或 DBeaver 创建两个数据库:
CREATE DATABASE order_db; CREATE DATABASE inventory_db;
4. 安装 Nacos(服务发现与配置中心)
Nacos 是阿里巴巴开源的服务管理平台,我们将用它来注册微服务。
- GitHub 下载地址:https://github.com/alibaba/nacos/releases
- 解压后进入 bin 目录运行:
startup.cmd -m standalone # Windows
sh startup.sh -m standalone # Mac/Linux
访问 http://localhost:8848/nacos 进入控制台,默认账号密码是 nacos/nacos
核心概念:通俗解释关键术语

为了帮助初学者快速理解,我们用生活中的例子来类比这些技术概念。
| 技术术语 | 通俗解释 | 类比生活 |
|---|---|---|
| 分布式事务 | 多个服务共同协作完成的一组操作 | 多个人一起搬沙发,要么一起抬起来,要么一起放下 |
| 本地事务 | 单一服务内的事务 | 自己写作业,错了可以撤回 |
| 全局事务 | 跨服务的大事务 | 整个公司一起做一个项目 |
| XA 模式 | 强一致性事务协议 | 公司开会,所有人达成共识才执行下一步 |
| TCC 模式 | Try - Confirm - Cancel,分阶段处理 | 订酒店流程:尝试预订 → 确认入住 → 取消房间 |
| Saga 模式 | 长时间运行的分布式事务,靠补偿机制恢复 | 退货流程:退款 → 回收货物 → 补偿运费 |
| 最终一致性 | 不求立即同步,但最终状态是对的 | 微信转账延迟几秒到账 |
接下来我们会结合具体的代码案例讲解最常用的几种模式。
实战项目:搭建一个简易的电商系统来演示分布式事务

第一步:创建两个微服务
我们将创建两个 Spring Boot 微服务:
order-service(订单服务)inventory-service(库存服务)
使用 Spring Initializr 快速创建项目
选择如下配置:
- Project: Maven
- Language: Java
- Spring Boot Version: 2.7.x
- Dependencies:
- Spring Web
- Spring Data JPA
- Lombok
- Nacos Discovery(可选)
- Feign Client(可选)
分别下载并解压两个项目,命名为 order-service 和 inventory-service。
第二步:配置数据库连接
以 order-service 为例,在 application.yml 中添加如下配置:
spring:
datasource:
url: jdbc:mysql://localhost:3306/order_db?useSSL=false&serverTimezone=UTC
username: root
password: your_password
driver-class-name: com.mysql.cj.jdbc.Driver
同理,为 inventory-service 设置对应的 inventory_db。
第三步:编写订单服务代码
1. 实体类 Order.java
@Entity
@Data
public class Order {
@Id
private String id;
private String productId;
private Integer quantity;
}
2. 控制器 OrderController.java
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private OrderRepository orderRepo;
@PostMapping
public ResponseEntity<?> createOrder(@RequestBody Order order) {
// 假设这里调用库存服务检查库存
// TODO: 添加 Feign 客户端调用库存服务
order.setId(UUID.randomUUID().toString());
orderRepo.save(order);
return ResponseEntity.ok("订单创建成功");
}
}
第四步:引入 Feign 实现服务间通信
为了让订单服务能调用库存服务,我们在 order-service 的启动类加上 Feign 注解:
@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
然后创建一个 Feign 客户端接口:
@FeignClient(name = "inventory-service")
public interface InventoryClient {
@PostMapping("/inventory/decrease")
ResponseEntity<String> decreaseInventory(@RequestParam("productId") String pid, @RequestParam("quantity") int qty);
}
第五步:实现库存服务逻辑
1. 实体类 Inventory.java
@Entity
@Data
public class Inventory {
@Id
private String productId;
private Integer stock;
}
2. 控制器 InventoryController.java
@RestController
@RequestMapping("/inventory")
public class InventoryController {
@Autowired
private InventoryRepository inventoryRepo;
@PostMapping("/decrease")
public ResponseEntity<String> decreaseStock(@RequestParam String productId, @RequestParam Integer quantity) {
Inventory inv = inventoryRepo.findById(productId).orElseThrow();
if (inv.getStock() < quantity) {
return ResponseEntity.status(400).body("库存不足");
}
inv.setStock(inv.getStock() - quantity);
inventoryRepo.save(inv);
return ResponseEntity.ok("库存减少成功");
}
}
引入分布式事务框架:Seata
现在问题来了:如果订单创建成功,但库存更新失败,怎么办?这时候就需要 Seata 来帮忙了!
Seata 是阿里巴巴开源的分布式事务解决方案。
第六步:安装并启动 Seata Server
GitHub 地址:https://github.com/seata/seata/releases
下载并解压后,修改配置文件 conf/file.conf,设置两个数据库的连接信息。
运行命令启动:
sh seata-server.sh -p 8091 -m db
第七步:在项目中集成 Seata 客户端
1. 引入依赖(Spring Boot 项目 pom.xml)
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.2</version>
</dependency>
2. 在配置文件中启用 Seata
seata:
enabled: true
application-id: order-service
tx-service-group: my_test_tx_group
3. 在订单控制器中使用 @GlobalTransactional
@PostMapping
@GlobalTransactional
public ResponseEntity<?> createOrder(@RequestBody Order order) {
// 调用库存服务
String result = inventoryClient.decreaseInventory(order.getProductId(), order.getQuantity());
if (!result.contains("成功")) {
throw new RuntimeException("库存不足");
}
order.setId(UUID.randomUUID().toString());
orderRepo.save(order);
return ResponseEntity.ok("订单创建成功");
}
这样,一旦出现异常,整个事务都会回滚,避免脏数据!
常见问题:新手容易遇到的问题和解答
Q1:为什么我调用另一个服务失败后没有回滚?
答:请检查是否开启了 @GlobalTransactional 注解,并确保 Seata Server 正常运行。此外,确保所有服务都正确配置了 file.conf 和 registry.conf 文件。
Q2:Seata 支持哪些数据库?
答:Seata 原生支持主流的关系型数据库如 MySQL、PostgreSQL、Oracle,也支持部分 NoSQL(需自定义适配器)。
Q3:TCC 和 Saga 有什么区别?
答:TCC 是一种预占资源的方式(比如先冻结金额),Saga 是一种事后补偿的方式(比如下单失败后退款)。前者更适用于高并发强一致性场景,后者更适合异步长周期操作。
Q4:我的事务总是卡住不动怎么办?
答:可能是数据库死锁或网络延迟导致。建议开启日志跟踪,查看具体哪一步卡住,也可以用 SHOW ENGINE INNODB STATUS; 查看数据库内部状态。
学习建议:下一步你可以学什么?
恭喜你完成了第一个完整的分布式事务实战!接下来你可以继续深入以下方向:
✅ 掌握更多分布式事务模型
| 模型 | 特点 | 适用场景 |
|---|---|---|
| XA | 强一致性 | 银行系统 |
| TCC | 业务补偿 | 金融交易 |
| Saga | 流程补偿 | 物流系统 |
| AT | 自动代理 | 新零售系统 |
推荐阅读文档:
- Seata 官方文档:https://seata.io
- Spring Cloud Alibaba 文档:https://github.com/alibaba/spring-cloud-alibaba
✅ 学习更多中间件配合使用
- RocketMQ(消息队列)
- Redis(缓存 + 分布式锁)
- RabbitMQ(异步任务处理)
✅ 实践大型项目
尝试构建一个完整的电商系统,包含:
- 用户服务
- 商品服务
- 库存服务
- 订单服务
- 支付服务
你可以部署到 Kubernetes 上,或者尝试 Serverless 架构!
结语
通过本教程,我们从零开始搭建了一个基于 Spring Boot + Seata 的分布式事务系统,学会了怎么保障多个服务之间的数据一致性。虽然分布式事务看起来复杂,但只要掌握了核心思想和常用工具,你就已经迈出了成为高级后端工程师的重要一步!
如果你觉得这篇文章对你有帮助,请记得分享给其他刚入门的朋友哦!
字数统计:约 3571 字

评论 0