分布式事务解决方案:最佳实践(面向初学者的教程)
开篇:什么是分布式事务?

你有没有遇到过这样的问题?比如你在淘宝买东西,钱已经扣了,但系统却告诉你“下单失败”;或者你在银行转账时,转出成功但到账失败。这些问题的背后其实都涉及到一个叫做分布式事务的问题。
那么,什么是分布式事务呢?
简单来说:
分布式事务 = 多个系统一起完成一件事 + 这件事要么全做,要么全不做
比如说,你在网上订机票加酒店,这两个服务是由不同的系统负责的。如果其中一个成功、另一个失败,就可能导致数据不一致(比如酒店定了但飞机票没订到)。这时候我们就需要一种机制来保证:这两个操作要么同时成功,要么都不发生。
分布式事务就是用来解决这个问题的关键技术。
环境准备:你需要哪些工具和环境

为了动手练习分布式事务的基础知识,我们使用以下技术栈(都是目前比较主流的):
- 操作系统:Windows / Linux / Mac OS(任意)
- Java 8+
- Spring Boot 2.x+
- MySQL 数据库
- RocketMQ(消息队列)
- Lombok(简化代码)
- Maven(项目构建)
步骤一:安装 Java 和 Maven
- 下载 JDK(Java 11 推荐)
官网地址:https://www.oracle.com/java/technologies/javase-jdk11-downloads.html - 安装后在终端输入命令:
如果能看到版本号说明安装成功。java -version mvn -v
步骤二:安装 MySQL
下载并安装 MySQL 8.x
安装完成后创建两个数据库用于我们的测试:
CREATE DATABASE bank_a;
CREATE DATABASE bank_b;
步骤三:安装 RocketMQ(消息队列)
下载地址:https://rocketmq.apache.org/
启动 RocketMQ 基本命令(详细步骤参考官方文档):
cd rocketmq-all-4.9.4-bin-release
start mqnamesrv
start mqbroker -n localhost:9876
确保服务能正常运行即可继续下一步。
步骤四:IDE 工具推荐(任选其一)
- IntelliJ IDEA(推荐)
- Eclipse + Lombok 插件
建议新手使用 IDEA 社区版,它对 Spring Boot 支持很好,并且有图形界面方便调试。
核心概念:搞懂这些词你就入门了

要理解分布式事务,首先得了解几个关键术语:
1. 事务(Transaction)
事务是数据库中的一个经典概念,它指的是一组操作,这组操作要么都成功,要么都失败。事务具备四个特性,统称 ACID:
- A(原子性):整个事务中的所有操作是一个整体,不能拆开。
- C(一致性):事务执行前后,数据库状态保持一致。
- I(隔离性):多个事务之间互不干扰。
- D(持久性):事务一旦提交,结果就会永久保存。
2. 分布式事务
当你的业务操作跨越多个系统或数据库时,就涉及到了分布式事务。
例如:
- A 系统从账号 a 转 50 元到账号 b(本地事务)
- B 系统把这次转账记录同步到日志服务器(另一个系统)
- 要求两个操作一起成功或一起失败 —— 这就是典型的分布式事务场景。
3. CAP 定理
CAP 是分布式系统的三大核心约束:
- C(一致性)
- A(可用性)
- P(分区容忍性)
CAP 定理指出:一个系统只能同时满足其中两项,另一项必须牺牲。
我们在设计分布式系统的时候会根据实际需求进行权衡。
4. 最终一致性 vs 强一致性
- 强一致性:所有节点在同一时间看到相同的数据。
- 最终一致性:允许数据暂时不同步,但最后会统一。
大多数互联网系统用的是“最终一致性”。
实战项目:用 RocketMQ 实现一个转账系统
现在我们来做一个最简单的案例:模拟跨数据库的转账操作,使用最终一致性 + RocketMQ的方式来实现分布式事务。
项目目标
我们要实现如下功能:
- 用户从 Bank A 的账户转 100 元到 Bank B 的账户。
- 两个账户分别属于两个数据库。
- 如果转账失败,两个账户的钱都不会变化。
步骤一:创建数据库表结构
进入 bank_a 数据库,创建用户表:
USE bank_a;
CREATE TABLE user_account (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
balance DECIMAL(10,2)
);
INSERT INTO user_account (name, balance) VALUES ('张三', 500);
进入 bank_b 数据库,创建同样的表:
USE bank_b;
CREATE TABLE user_account (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
balance DECIMAL(10,2)
);
INSERT INTO user_account (name, balance) VALUES ('李四', 300);
步骤二:Spring Boot 项目搭建
用 IDEA 创建 Spring Boot 项目,添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
步骤三:配置多数据源
在 application.yml 中添加两个数据库连接信息:
spring:
datasource:
dynamic:
primary: master
datasource:
master:
url: jdbc:mysql://localhost:3306/bank_a?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
url: jdbc:mysql://localhost:3306/bank_b?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
注意:这里用的是
dynamic-datasource-spring-boot-starter这个第三方组件,记得加入对应的 starter。
步骤四:定义基本 Service 层
BankAService.java
@Service
@DS("master")
public class BankAService {
@Autowired
private JdbcTemplate jdbcTemplate;
public void deductMoney(String name, double amount) {
String sql = "UPDATE user_account SET balance = balance - ? WHERE name = ?";
jdbcTemplate.update(sql, amount, name);
}
}
BankBService.java
@Service
@DS("slave")
public class BankBService {
@Autowired
private JdbcTemplate jdbcTemplate;
public void addMoney(String name, double amount) {
String sql = "UPDATE user_account SET balance = balance + ? WHERE name = ?";
jdbcTemplate.update(sql, amount, name);
}
}
TransferService.java
@Service
public class TransferService {
@Autowired
private BankAService bankAService;
@Autowired
private BankBService bankBService;
@Autowired
private RocketMQTemplate rocketMQTemplate;
public void transfer(String from, String to, double amount) {
try {
// 第一步:A 减钱
bankAService.deductMoney(from, amount);
// 第二步:发送 RocketMQ 消息通知 B 加钱
Message<String> message = MessageBuilder.withPayload("ADD:" + to + ":" + amount).build();
rocketMQTemplate.send(message, new CorrelationIdCallback() {
@Override
public String getCorrelationId(Message<?> message) {
return UUID.randomUUID().toString();
}
});

} catch (Exception e) {
System.out.println("转账失败,回滚未处理");
}
}
}
这只是一个简单示例,真正的补偿逻辑、消费端处理等内容我们下面补充。
步骤五:RocketMQ 消费端逻辑(BankB 模块)
在消费者一方编写监听器:
@Component
@RocketMQMessageListener(topic = "TRANSFER_TOPIC", consumerGroup = "transfer-group")
public class BankBConsumer implements RocketMQListener<String> {
@Autowired
private BankBService bankBService;
@Override
public void onMessage(String msg) {
if (msg.startsWith("ADD:")) {
String[] parts = msg.split(":");
String name = parts[1];
double amount = Double.parseDouble(parts[2]);
bankBService.addMoney(name, amount);
}
}
}
测试:运行整个流程
你可以通过 Controller 或者 Postman 发起一个转账请求:
@RestController
public class TransferController {
@Autowired
private TransferService transferService;
@GetMapping("/transfer")
public String doTransfer() {
transferService.transfer("张三", "李四", 100);
return "转账开始";
}
}
访问:http://localhost:8080/transfer
可以看到:
- 张三的余额减少了 100 元
- 李四的余额增加了 100 元
这样,我们就完成了基于 RocketMQ 的“半消息+本地事务”的一个典型应用场景!
常见问题:为什么我的事务有时候失败了数据还变了?
以下是新手最容易遇到的几个问题,以及它们的解答:
✅ 问题 1:两个操作不是同时成功的怎么办?
回答:我们这里是“异步补偿”的方式,即先减钱再发消息,然后由消息队列去触发加钱。如果消息丢失或失败,我们需要额外加“事务日志”和“定时重试”机制才能保障最终一致性。
✅ 问题 2:事务中间失败,如何回滚?
回答:传统数据库事务可以自动回滚,但分布式事务中没有单一事务管理器。你需要自己手动写“补偿”逻辑,比如调用另一个接口来回退操作(如退款、取消订单等)。
✅ 问题 3:RocketMQ 没收到消息怎么办?
回答:可以开启 RocketMQ 的事务消息机制,或者采用延迟重试策略,比如每隔一段时间尝试重新投递。
学习建议:进阶路线图
恭喜你完成了这个项目的演练!接下来,我建议你按以下路径继续学习:
掌握 Seata 的使用
阿里开源的分布式事务框架 Seata 支持 AT 模式、TCC 模式等,适合企业级项目。了解 SAGA 模式与 TCC 模式对比
不同业务场景选择不同模式,比如金融交易更适合 TCC,而订单处理更常用 SAGA。深入 RocketMQ 事务消息机制
了解 RocketMQ 提供的“本地事务 + 回查”机制,实现更安全的异步通知。实战微服务项目整合
尝试用 Spring Cloud Alibaba + Seata 构建一个完整的微服务架构下的分布式事务系统。性能优化与故障排查技巧
包括幂等性设计、重复消费、死信队列处理等内容。
总结
在这篇教程中,我们从零基础出发,一步步讲解了:
- 分布式事务的基本概念
- 所需的开发环境搭建
- 使用 RocketMQ 实现一个简单的转账系统
- 初学者常见的疑问及解答
- 后续学习的方向建议
虽然分布式事务听起来很复杂,但它本质上就是在多个服务间协调数据的一致性。只要掌握了原理和常用方案,就可以应对大部分业务场景的需求。
如果你喜欢这篇教程,欢迎收藏并分享给其他初学的朋友!也欢迎在评论区留言,提出你的问题或想法 😊

评论 0