分布式事务解决方案:最佳实践(适合零基础新手)
开篇:什么是分布式事务?它用来做什么?

在传统的单体应用中,我们的程序运行在一个服务器上,数据库也在本地。这种情况下,处理事务(比如转账、下单等操作)非常简单,可以用一个 BEGIN TRANSACTION 和 COMMIT 就搞定了。
但现代互联网系统越来越复杂了。为了提高性能和可扩展性,人们通常把系统拆分成多个服务,每个服务各自管理自己的数据库。比如:
- 用户服务:负责管理用户数据
- 支付服务:负责处理支付逻辑
- 订单服务:负责创建订单信息
当我们需要跨多个服务进行一次完整的操作时(比如下单并支付),就会遇到一个很头疼的问题:怎么保证这些操作要么全成功,要么全失败?
这就是“分布式事务”要解决的问题。
通俗理解: 如果你去商场买东西,收银员告诉你先拿货再付款。那你肯定不干对吧?我们希望整个过程是可靠的 —— 要么买到东西也完成了付款,要么什么都没发生。
同样的,在多个微服务之间,我们也希望确保数据的一致性。
环境准备:开发环境搭建指南(适合初学者)

在开始动手之前,我们需要准备好一些工具。
所需软件列表:
| 工具 | 用途说明 |
|---|---|
| Java 17 或更高版本 | 编写后端服务 |
| Maven | 项目依赖管理 |
| IntelliJ IDEA / VS Code | IDE |
| MySQL 8+ | 数据库 |
| Nacos(服务注册中心) | 微服务之间的协调 |
| Seata(分布式事务框架) | 管理事务一致性 |
| Docker(非必须) | 部署中间件 |
安装步骤(简化版):
第一步:安装 JDK
- 下载地址:https://www.oracle.com/java/technologies/downloads/
- 设置环境变量:
JAVA_HOME=C:\Program Files\Java\jdk-21- 添加
%JAVA_HOME%\bin到Path
第二步:安装 MySQL
- 推荐使用 XAMPP、WAMP 或直接下载 MySQL 官方安装包。
- 启动服务后,用 Navicat 新建两个数据库:
order_dbpayment_db
第三步:安装 Nacos
- GitHub 地址:https://github.com/alibaba/nacos
- Windows 用户可以解压后进入 bin 目录,运行:
startup.cmd -m standalone - 默认访问地址:http://localhost:8848/nacos,默认用户名密码都是
nacos
第四步:安装 Seata
- 下载地址:https://github.com/seata/seata/releases
- 解压后修改配置文件:
registry.conf: 设置 mode 为 nacos,并填写对应的 IP- 各个服务的配置文件添加 seata-client 配置
- 运行:
./seata-server.sh
如果你嫌麻烦,也可以用 Docker 一键启动所有环境(这里就不展开讲了)。
核心概念:快速掌握关键术语(通俗解释)
以下是几个你在学习分布式事务过程中会频繁接触到的概念。
1. 分布式事务
拆分后的多个服务之间,执行一组有因果关系的操作,保证它们整体成功或失败。
2. 事务 ACID 特性
- A(原子性):要么全做,要么都不做
- C(一致性):事务前后数据状态保持一致
- I(隔离性):并发执行互不影响
- D(持久性):一旦提交就不会丢失
但在分布式系统里,ACID 是难以完全满足的。
3. CAP 原则
Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容忍性) 在一个分布式系统中,最多只能同时满足两项。
我们要根据实际业务选择侧重哪一个。
4. 两阶段提交协议(2PC)
一种经典的分布式事务解决方案,分为两个阶段:
- 准备阶段:协调者问所有参与者是否可以提交
- 提交阶段:如果都同意就提交,否则回滚
缺点:同步等待时间长,协调者故障会影响全局
5. TCC 模型(Try-Confirm-Cancel)
一种柔性事务模型,流程如下:
- Try:资源预留(冻结库存、预扣款)
- Confirm:确认执行(真正减库存、扣款)
- Cancel:取消操作(释放库存、退回款项)
优点:性能好、支持高并发,适合电商类场景
实战项目:从头开始实现一个简单的分布式事务系统
我们来做一个小项目:用户下订单并完成支付。涉及两个独立的服务:
- OrderService(订单服务)
- PaymentService(支付服务)
我们将用 Seata + Spring Boot + TCC 模式 来实现。
第一步:新建 Spring Boot 项目
创建两个项目,分别命名为 order-service 和 payment-service。
使用 Spring Initializr 创建项目:
- 选 Spring Web、Spring Data JPA、MySQL Driver、Nacos Discovery、Seata Starter
第二步:编写实体类 & Repository
以订单表为例:
@Entity
public class Order {
@Id
private String id;
private String userId;
private BigDecimal amount;
private String status; // CREATED, PAID
}
Payment 类似,不做赘述。
第三步:配置 Seata 并启用服务注册发现
在 application.yml 中添加 Seata 配置:
seata:
enabled: true
application-id: order-service
tx-service-group: my_group
service:
vgroup-mapping:
my_group: default
并在主类添加注解:
@EnableDiscoveryClient
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
第四步:TCC 模式核心代码示例
假设用户下单时先检查余额是否足够,再冻结资金,接着创建订单,最后确认支付。
PaymentService 的 Try 方法:
@TwoPhaseBusinessAction(name = "deductMoney")
public boolean deductMoney(BusinessActionContext ctx, @BusinessActionContextParameter(paramName = "userId") String userId,
@BusinessActionContextParameter(paramName = "amount") BigDecimal amount) {
// 冻结金额,不实际扣除
User user = userRepository.findById(userId).get();
if (user.getBalance().compareTo(amount) < 0) {
return false;
}
user.setFrozenAmount(user.getFrozenAmount().add(amount));
userRepository.save(user);
return true;
}
Confirm 阶段:
@Commit
public boolean confirmDeductMoney(BusinessActionContext ctx) {
String userId = (String) ctx.getActionContext("userId");
BigDecimal amount = (BigDecimal) ctx.getActionContext("amount");
User user = userRepository.findById(userId).get();
user.setBalance(user.getBalance().subtract(amount));
user.setFrozenAmount(user.getFrozenAmount().subtract(amount));
userRepository.save(user);
return true;
}
Cancel 阶段:
@Rollback
public boolean cancelDeductMoney(BusinessActionContext ctx) {
String userId = (String) ctx.getActionContext("userId");
BigDecimal amount = (BigDecimal) ctx.getActionContext("amount");
User user = userRepository.findById(userId).get();
user.setFrozenAmount(user.getFrozenAmount().subtract(amount)); // 解冻
userRepository.save(user);
return true;
}
第五步:调用方式加注解开启事务
在 Controller 层:
@GetMapping("/createOrder")
@GlobalTransactional
public String createOrder(@RequestParam String userId, @RequestParam BigDecimal amount) {
String orderId = UUID.randomUUID().toString();
// Step 1: 减少用户余额(调用 payment 服务)
paymentFeignClient.deductMoney(userId, amount);
// Step 2: 创建订单
Order order = new Order();
order.setId(orderId);
order.setUserId(userId);
order.setAmount(amount);
order.setStatus("CREATED");
orderRepository.save(order);
return "下单成功:" + orderId;
}
当任何一个服务出错,整个流程都会回退(Cancel 触发),保证数据一致性!
常见问题与解答
Q1:为什么我的分布式事务没生效?
可能原因:
- Seata 没有正确配置,没有连接到 TC(事务协调器)
- 没有使用正确的注解
@GlobalTransactional - 数据库未使用支持的 JDBC 数据源(建议使用 Druid、Hikari)
Q2:Seata 报错说找不到 transaction group 怎么办?
答:检查 file.conf 和 application.yml 中的 tx-service-group 是否一致,以及注册中心是否正常运行。
Q3:TCC 的 Confirm 和 Cancel 方法没有触发?
答:确保业务方法抛出异常才会触发 Cancel,正常返回不会自动 rollback。
学习建议:下一步该学什么?

恭喜你完成了第一个分布式事务实战项目!接下来你可以沿着以下路径继续深入学习:
1. 学习其他分布式事务方案
- Saga 模式:适用于长周期操作,如退款、物流调度
- 最终一致性 + 补偿机制:结合定时任务校验数据
- RocketMQ 事务消息:适用于异步场景下的事务处理
2. 学习 Seata 的 AT 模式
- 不改动业务代码即可实现分布式事务
- 通过代理数据库实现 SQL 拦截与回滚日志记录
3. 结合真实项目练手
尝试集成到你的毕业设计或者开源项目中,例如:
- 电商系统(下单、支付、库存同步)
- 在线教育平台(报名、课程分配、积分更新)
4. 学习分布式系统基础理论
- Paxos、Raft 协议
- CAP 与 BASE 理论
- 服务降级与熔断(Hystrix、Sentinel)
结语

分布式事务是一个入门门槛稍高的技术点,但它是构建高可用、高性能分布式系统的基石。
本文带你一步步从零搭建了一个基于 TCC 的分布式事务系统,希望你能动手实践,加深理解。后续可以根据自身兴趣,继续探索更高级的分布式架构模式。
坚持练习,你就一定能在后端开发这条路上越走越稳 🚀
💡 温馨提示:完整代码示例可在 GitHub 搜索相关 Seata 教程项目获取。你也可以试着 fork 一份 starter-template 来加速开发。
如有任何问题,欢迎在下方留言提问 😊

评论 0