分布式事务解决方案:踩坑之后的总结与最佳实践
开篇:为什么分布式事务这么重要?

作为后端开发人员,尤其是负责核心业务系统的技术负责人,我深刻体会到一个项目从单体架构走向微服务时带来的巨大挑战。其中,分布式事务绝对是我这几年踩过最多“坑”的领域之一。
2019年我接手了一个金融类项目,原本是一个Spring Boot单体应用,随着业务增长和团队扩张,我们决定将其拆分成多个微服务模块,分别负责订单、库存、支付、积分等。但拆完后第一个摆在面前的问题就是:“下单同时扣减库存和积分,这中间如何保证数据一致性?”
这个问题看起来简单,但其实背后隐藏了巨大的复杂性。今天我想通过我真实项目的经历,分享一套我们在实际生产中打磨出来的分布式事务解决方案,并把那些踩过的坑一一道来。
问题描述:业务场景与挑战


我们的系统里有个很典型的业务流程:
用户下单 -> 创建订单 -> 减库存 -> 扣积分 -> 支付完成
在单体系统下,所有这些操作都在一个数据库事务中完成,非常安全可靠。可是一旦服务拆分,这些操作就分布到了不同的服务里,各自有自己的数据库实例,这时候一旦某个步骤失败,整个流程就可能处于不一致状态。
比如:
- 库存已经扣除,但是积分未扣成功;
- 积分扣除了,但订单没有生成或支付失败;
- 消息队列消息丢了,导致某一步没执行。
我们当时尝试过几个方案:
- 本地事务 + 异步补偿:手动实现补偿逻辑,代码冗长且难维护;
- 基于消息队列的最终一致性:数据会短暂不一致,需要容忍时间差;
- 两阶段提交(2PC):引入Seata框架,但在高并发下性能差且存在阻塞风险;
- 最后我们逐步演进到以 Saga模式 + 补偿机制 + 状态机驱动 为主的方式。
解决方案:Saga + 状态机 + 补偿机制的组合拳

我们最后选择的是Saga模式结合状态机驱动的补偿事务机制。这个方案虽然不是银弹,但在我们项目中表现得非常好。
Saga模式简介
Saga是分布式事务的一种经典模式,它的核心思想是:
将整个事务拆分为多个本地事务,每个本地事务执行一个操作。如果其中任何一步失败,则按顺序回滚之前的操作。
比如下单流程可以定义为:
Step 1: 创建订单
Step 2: 扣库存
Step 3: 扣积分
Step 4: 调用支付接口
每一步都需要有对应的补偿动作:
Compensate 1: 取消订单
Compensate 2: 回退库存
Compensate 3: 回退积分
Compensate 4: 取消支付
这样即使某一步失败,也能通过“逆向”操作把前面已经成功的部分撤销掉。
我们的设计思路
- 每个服务只处理自己的本地事务
- 使用事件总线(如RabbitMQ/Kafka)进行通信
- 引入状态机驱动流程流转,防止流程混乱
- 记录事务上下文,包括事务ID、当前步骤、已执行步骤、补偿日志等
- 补偿失败则进入人工干预流程(如告警+重试)
核心组件结构图如下:
+----------------+ +--------------+ +----------------+
| Order Service | -----> | Event Bus | <-----> | Stock Service |
+-------+--------+ +------+-------+ +-------+--------+
| | |
| | |
v v v
+----------------+ +--------------+ +----------------+
| Point Service | <----- | StateMachine | ------> | Payment Gateway|
+----------------+ +--------------+ +----------------+
代码实践:关键逻辑示例

以下是我们在订单服务中触发一个完整 Saga 流程的核心逻辑片段(简化版):
// 触发下单流程
public void placeOrder(Long userId, Long productId) {
String transactionId = UUID.randomUUID().toString();
// Step 1: 创建订单
Order order = createOrder(userId, productId, transactionId);
eventBus.publish(new OrderCreatedEvent(order, transactionId));
}
// 监听订单创建事件,触发扣库存
@OnEvent("OrderCreatedEvent")
public void onOrderCreated(OrderCreatedEvent event) {
boolean success = stockService.deductStock(event.getProductId(), 1);
if (success) {
eventBus.publish(new StockDeductedEvent(event.getTransactionId()));
} else {
eventBus.publish(new StockDeductionFailedEvent(event.getTransactionId()));
}
}
// 监听库存扣减失败,开始补偿链路
@OnEvent("StockDeductionFailedEvent")
public void onStockDeductionFailed(String transactionId) {
log.warn("扣库存失败,启动补偿流程");
// 回退订单
orderService.cancelOrder(transactionId);
// 告警通知人工介入
alert.notify("分布式事务中断", "transactionId=" + transactionId);
}
这套逻辑的关键点在于:
- 所有操作都通过异步事件驱动
- 每个服务只需关注自己这一层的状态变更
- 使用统一的事务ID串联整个流程
- 失败时自动发起逆向补偿流程
踩坑经验:那些年我们遇到的坑和填法
坑1:事件丢失怎么办?
一开始我们使用 RabbitMQ 进行事件广播,有一次因为网络抖动,订单创建事件丢了,导致整个流程卡死。
解决方法:
- 生产端开启 confirm 模式,确保消息被 Broker 正确接收
- Broker 配置持久化和镜像队列,避免消息丢失
- 消费端做好幂等控制(比如检查是否已处理过该 transactionId)
坑2:补偿动作本身失败怎么办?
最怕的情况是:某个补偿操作也失败了,比如你去回退积分的时候失败了,怎么办?
解决办法:
- 每次执行补偿前都落盘记录,失败自动标记为待修复状态
- 后台定时任务扫描这类事务,进行重试或通知人工介入
- 在补偿逻辑中加入重试机制(比如最多3次重试)
坑3:状态管理混乱,流程跳步
刚开始我们只是用一个简单的字段表示“状态”,后来发现随着流程增多,状态很容易乱。
改进方式:
- 使用 状态机引擎 来驱动流程流转
- 将状态迁移规则写成配置(如JSON),方便扩展
- 结合有限状态机设计工具(如Squirrel-Foundation)
效果总结:这套方案给我们带来了什么?
上线后半年内,这套 Saga + 补偿机制支撑了每天近百万级的交易量。下面是几个关键指标的变化:
| 指标 | 上线前 | 上线后 | 提升幅度 |
|---|---|---|---|
| 数据不一致率 | ~3% | <0.01% | ✅ 降低 |
| 系统响应时间 | 平均400ms | 平均250ms | ✅ 提高 |
| 线上故障率 | 1-2次/周 | <1次/月 | ✅ 显著下降 |
| 日志追踪效率 | 低 | 高 | ✅ 改善明显 |
更重要的是:
- 团队协作更加顺畅:大家不再担心跨服务调用导致的数据一致性问题。
- 运维更轻松:有了状态机和事务上下文,排查问题清晰了很多。
- 可扩展性强:新服务接入 Saga 流程只需要注册补偿方法即可。
经验分享:给还在路上的你几点建议

如果你也在做分布式事务相关的工作,以下几点是我强烈推荐的:
1. 不要一开始就追求强一致性
大多数业务场景允许“最终一致性”。与其花大力气实现强一致性,不如优化补偿机制和状态追踪。
2. 让事务流程可视化很重要
建议你们把 Saga 的流程图做成可配置的 JSON 或 BPMN,这样业务方和研发都能看懂,沟通成本大大降低。
3. 每个动作都要有补偿动作,不能遗漏
这一点极其关键!否则你会遇到“半途而废”的事务越来越多,最后变成“脏数据黑洞”。
4. 适当引入 TCC 或 SAGA 中间件,节省开发成本
比如阿里开源的 Seata 和 Netflix 的 Conductor,都是不错的起点。
5. 监控和报警一定要跟上
分布式事务链条复杂,必须要有完整的监控链路。建议你:
- 对每个事务ID生成 trace-id,贯穿所有服务
- 对关键操作打日志并采集到ELK中
- 设置定时巡检补偿失败的事务
- 配置异常事务自动告警到钉钉/企业微信
写在最后:技术是手段,稳定才是目的
分布式事务从来不是一个单纯的技术问题,它涉及到架构设计、系统集成、错误恢复等多个层面。我在项目初期也走了不少弯路,甚至一度考虑放弃微服务架构回归单体。
但现在回头看,那几个月的“痛苦”过程让我对系统的健壮性和容错能力有了更深的理解。希望这篇文章能帮你在面对类似问题时少走一些弯路,也希望你能在实践中不断打磨出适合你们业务场景的最佳方案。
毕竟,技术没有最好的,只有最适合的。
如有任何问题或想交流具体细节,欢迎随时联系我!
如果你觉得这篇文章对你有帮助,不妨点赞、收藏或者转发给团队一起学习~ 🚀

评论 0