分布式事务解决方案:最佳实践
开篇:分布式事务是什么?为什么重要?

你有没有这样的经历?当你在网上下单的时候,系统会同时扣减库存、生成订单,并可能触发支付。如果这个过程只发生在一台服务器上,事情还好处理;但如果这些操作分散在多个服务之间呢?比如订单服务调用库存服务和支付服务,它们各自管理自己的数据库。这种情况下,如何保证整个流程要么全部成功,要么一起回滚呢?这就是“分布式事务”要解决的问题。
简单来说,分布式事务就是跨多个节点(或服务)的数据操作,需要确保一致性。就像转账一样,A给B转钱,既要从A账户扣款,又要给B账户加钱,两个操作必须同步完成——失败了就要一起回滚。
传统单体应用中,我们使用数据库的事务机制就能很好地控制一致性,但在微服务架构下,这就变得复杂多了。这也是很多初学者遇到的难点之一。
本文将带你一步步了解分布式事务的核心概念,并手把手实现一个简单的案例,帮助你在实践中掌握这项关键技术。
环境准备:搭建开发环境

我们需要什么?
为了让本项目能够顺利运行,你需要以下几个工具:
- Java 8 或更高版本
- Maven(用于依赖管理)
- Spring Boot(构建微服务的基础框架)
- Redis 或 RabbitMQ(可选,用于消息中间件)
- MySQL(至少两个库,模拟两个独立数据库)
📝 说明: 如果你想更快上手,也可以使用Docker来部署数据库服务。
安装步骤简要如下:
安装 JDK
下载地址:https://www.oracle.com/java/technologies/javase-downloads.html
安装完成后,在终端输入java -version和javac -version验证是否成功。安装 Maven
下载地址:https://maven.apache.org/download.cgi
解压后配置环境变量,执行mvn -v查看版本号。下载并解压 Spring Boot 项目模板
可以访问 Spring Initializr 创建基础项目。添加以下依赖:- Spring Web
- Spring Data JPA
- Lombok(简化代码)
- Spring Cloud Alibaba Seata(后续用到)
创建两个 MySQL 数据库
CREATE DATABASE order_db;
CREATE DATABASE inventory_db;
- 验证数据库连接
修改application.yml文件中的数据库配置信息,确保能连上刚刚创建的两个库。
现在,我们的开发环境已经就绪。接下来进入正题!
核心概念:用最通俗的语言解释专业术语
在正式写代码之前,我们需要先了解几个关键概念,这样可以帮助你更好地理解分布式事务的原理。
一、ACID 是啥?
这是我们在单机数据库中最常听到的概念:
A:原子性(Atomicity) —— 事务里的所有操作要么全做,要么全不做
C:一致性(Consistency) —— 数据不能出现非法状态
I:隔离性(Isolation) —— 多个事务并发执行时互不干扰
D:持久性(Durability) —— 一旦提交,数据就不会丢失
这四个特性是数据库事务的核心,但在分布式环境下,我们要面对更复杂的挑战。
二、CAP 原理又是什么?
CAP 是分布式系统的三大基石特征:
- 一致性(Consistency):数据在多节点间保持一致
- 可用性(Availability):请求总能得到响应
- 分区容忍性(Partition Tolerance):系统在网络分区的情况下仍能正常工作
根据 CAP 定理,三者只能选其二。所以,在设计分布式事务方案时,通常要在一致性与可用性之间做取舍。
三、什么是两阶段提交(2PC)?
这是最早的一种分布式事务协议,分为两个阶段:
- 准备阶段(Prepare):协调者询问所有参与者是否准备好提交事务。
- 提交阶段(Commit):如果全部同意,则提交,否则回滚。
优点:一致性高。
缺点:效率低,存在单点故障风险。
四、Seata 是什么?
Seata 是阿里巴巴开源的一个分布式事务中间件,支持 AT 模式(自动补偿)、TCC 模式(尝试-确认-取消)、SAGA 模式等。
它通过全局事务 ID(XID)来协调各个服务之间的事务一致性,是一个非常实用的工具。
我们将使用 Seata 来实现我们的实战项目。
实战项目:跟着教程一步步完成简单案例
目标:实现一个简单的电商下单业务场景,包含两个服务:
- 订单服务(Order Service):负责生成订单
- 库存服务(Inventory Service):负责扣减库存
我们希望这两个操作要么都成功,要么都失败,保证一致性。
步骤一:创建项目结构
使用 Spring Initializr 创建两个模块:
order-serviceinventory-service
每个模块分别连接自己的数据库。
步骤二:添加 Seata 依赖
在每个项目的 pom.xml 中添加如下依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2022.0.0.0</version>
</dependency>
并在 application.yml 中启用 Seata:
seata:
enabled: true
tx-service-group: my_test_tx_group
service:
vgroup-mapping:
my_test_tx_group: default
grouplist:
default: 127.0.0.1:8091
这里我们使用本地 Seata Server,端口为 8091。
步骤三:启动 Seata Server
你可以从 GitHub 获取 Seata 的发布包,解压后运行:
sh seata-server.sh -p 8091 -m file
默认使用文件模式保存事务日志。
步骤四:编写接口和服务逻辑
1. 库存服务(InventoryService)
@Service
public class InventoryService {
@Autowired
private InventoryRepository inventoryRepo;
@GlobalTransactional // 开启全局事务
public boolean deductInventory(String productId, int quantity) {
Inventory inventory = inventoryRepo.findByProductId(productId);
if (inventory.getStock() < quantity) {
return false; // 库存不足
}
inventory.setStock(inventory.getStock() - quantity);
inventoryRepo.save(inventory);
return true;
}
}

2. 订单服务(OrderService)
@Service
public class OrderService {
@Autowired
private InventoryClient inventoryClient; // 调用库存服务的 Feign 客户端
@Autowired
private OrderRepository orderRepo;
@GlobalTransactional
public String createOrder(String userId, String productId, int quantity) {
boolean success = inventoryClient.deductInventory(productId, quantity);
if (!success) {
throw new RuntimeException("库存不足");
}
Order order = new Order();
order.setUserId(userId);
order.setProductId(productId);
order.setQuantity(quantity);
order.setStatus("CREATED");
orderRepo.save(order);
// 测试异常
if (quantity > 5) {
throw new RuntimeException("人为制造失败");
}
return "订单创建成功";
}
}
3. 控制器层(Controller)
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping
public ResponseEntity<?> placeOrder(@RequestBody OrderRequest request) {
try {
String result = orderService.createOrder(
request.getUserId(),
request.getProductId(),
request.getQuantity()
);
return ResponseEntity.ok(result);
} catch (Exception e) {
return ResponseEntity.status(500).body("下单失败:" + e.getMessage());
}
}
}

步骤五:测试功能
使用 Postman 或 curl 发送请求:
POST http://localhost:8080/orders
{
"userId": "u1",
"productId": "p1",
"quantity": 3
}
正常情况下应该返回“订单创建成功”。
再试试传入数量大于库存的值或者设为 6,看看是否会抛出错误并且回滚库存减少。
常见问题解答(FAQ)
❓ Q1:我用了 Seata,但事务没有生效怎么办?
答: 检查是否做了以下几件事:
- 是否在方法上正确使用了
@GlobalTransactional注解? - Seata Server 是否正在运行?
application.yml中的 Seata 配置是否正确?- 使用的是支持 Seata 的数据库驱动吗?是否开启了 AT 模式?
❓ Q2:我在测试时看到事务表被锁住了,会不会影响性能?
答: 在 Seata 的 AT 模式中,确实会对涉及的数据行加锁,避免并发修改。但在正常业务中只要控制好事务粒度,一般不会造成严重性能问题。建议合理划分业务边界,避免长事务。
❓ Q3:除了 Seata,还有哪些分布式事务解决方案?
答: 主流的还有:
- Saga 模式:适用于长时间运行的任务,通过反向补偿机制恢复失败操作。
- TCC 模式:需要开发者手动实现 Try、Confirm、Cancel 三个阶段。
- 消息队列+本地事务表:结合 RocketMQ 等中间件,适合异步场景。
每种方案各有优劣,应根据业务场景选择合适的方案。
学习建议:下一步该怎么学?
恭喜你完成了第一个分布式事务项目的实践!如果你还想继续深入学习,这里有一些推荐的学习路径:
✅ 初级阶段(你现在所处的位置):
- 掌握 Seata 的基本使用(AT 模式)
- 理解 CAP 原则与 BASE 理论
- 熟悉常见的分布式事务模型(2PC、TCC、Saga)
✅ 中级阶段:
- 实践更多业务场景,如支付、积分兑换等
- 学习使用消息队列(如 Kafka 或 RocketMQ)配合本地事务表
- 探索 Saga 模式在复杂流程中的应用
✅ 高级阶段:
- 研究 Seata 源码,了解其底层原理
- 设计高可用、高性能的分布式事务方案
- 结合 DDD(领域驱动设计)进行事务建模
总结
分布式事务是现代系统架构中不可或缺的一环。它让微服务之间可以协作完成复杂的业务流程,而不会因为某个环节失败导致系统状态混乱。
本篇文章从零开始,带你一步步搭建环境、编写代码,并通过实际案例演示 Seata 的使用方式。希望这篇文章能成为你通往高手之路的第一步!
继续加油,下一章我们可能会深入讲解 TCC 模式或者 Saga 模式的实战案例,敬请期待~

评论 0