分布式事务实战:我们是怎么趟过深水区的
大家好,我是阿杰,一名全栈开发者。过去几年主要在做一些电商平台、支付系统以及中台相关的开发工作。随着业务发展,微服务架构成了标配,而分布式事务,就像一个甩不掉的“老熟人”,总是冷不丁地出现在关键节点上。
今天我想聊聊我们团队在做订单履约系统重构时,遇到的一次典型的分布式事务问题,以及我们是如何一步步解决它的。这个过程并不轻松,踩过坑、试过错、也推翻过方案,但最终走通了,并且在上线后稳定运行了一年多时间。希望这篇文章能给你带来一些启发,少走点弯路。
项目背景:订单履约系统的升级

事情要从2022年下半年说起。那会儿公司准备对订单履约系统进行一次大的重构。原系统是单体架构,所有模块(订单、库存、物流、财务)都挤在一个数据库里。随着业务增长,数据量和并发请求越来越高,系统响应变慢,数据库锁竞争严重,经常出现超时甚至死锁的问题。
我们决定采用微服务架构来解耦各个子系统:
- 订单中心(Order Center)
- 库存中心(Inventory Center)
- 物流中心(Logistics Center)
- 财务中心(Finance Center)
这下确实灵活了,可问题也接踵而来——当用户下单后,我们需要依次调用库存系统扣减库存、物流系统预占资源、财务系统冻结资金。而如果其中一个步骤失败,比如库存扣减成功但物流失败,整个流程该如何回退?
传统的本地事务已经不管用了,这就涉及到我们今天的主题:分布式事务的解决方案落地实践。
遇到的挑战


场景一:扣库存失败如何回滚?
最开始我们尝试使用远程调用来实现事务一致性。例如:
下单 -> 扣减库存(RPC) -> 创建物流订单(RPC) -> 冻结资金(RPC) -> 返回成功
但很快我们就发现,这种方式存在明显的可靠性问题:
- 如果“创建物流订单”失败,库存已经扣掉了,怎么办?
- 如果“冻结资金”失败,前面的操作是否要撤销?
我们尝试加了一些“反向补偿”的逻辑,即每次操作之后记录状态,如果后续失败再调用相应的 rollback 接口。这种做法虽然短期能应付需求,但在高并发下容易导致数据混乱,特别是在网络波动或下游系统异常的情况下。
场景二:消息队列异步处理的丢失风险
为了提高性能,我们后来改用异步处理的方式,通过 Kafka 将各个系统的变更通知广播出去,各自消费消息执行业务逻辑。比如:
订单服务写入数据库 -> 发送「扣减库存」消息 -> 库存服务消费并扣库存
这种方法虽然提升了吞吐量,但却带来了新的问题:
- 消息发送和本地事务无法保证原子性,可能出现订单已提交但消息未发出
- 消费端处理失败或重复消费时,缺乏统一的状态追踪机制
- 系统之间状态不同步,难以调试和排查问题
场案三:SAGA 模式的局限性
我们也试过引入 SAGA 模式,把每个步骤设计成有正向处理和补偿动作的小事务。比如:
[Begin Saga]
Step1: 扣减库存(Try)
Step2: 预占物流资源(Try)
Step3: 冻结资金(Try)
...
任何一步失败,就依次调用 Cancel 方法
这个模式在理论上讲得通,但在实践中发现几个大坑:
- 补偿操作必须幂等且无副作用,否则可能导致二次出错
- 中间状态需要额外存储和管理,增加了系统复杂度
- 补偿链可能很长,影响系统响应时间,用户体验差
- 对于金融类操作(如退款),SAGA 不太合适,因为涉及真实资金流动,不能简单“反向操作”
这些方案看似都能解决问题,但都有明显短板。我们意识到,真正要在分布式系统中实现一致性,需要一套更清晰、更具工程可行性的解决方案。
解决方案选型与实施

经过技术选型和反复权衡,我们最终选择了 Seata + RocketMQ 消息事务 + 最终一致性补发机制 的混合方案。以下是具体实现思路:
架构图简述(伪代码版)
[订单中心] → Seata 全局事务 → [库存中心] → [物流中心] → [财务中心]
↓ ↑
[RocketMQ消息] [确认/补偿]
我们围绕 Seata 做核心事务控制,对于部分非强一致性场景,结合 RocketMQ 的事务消息能力,实现异步最终一致性的保障。
核心事务流程:使用 Seata 实现 AT 模式
我们将订单的核心流程(下单、锁库存、生成物流单、冻结资金)纳入 Seata 的全局事务中。
举个例子,在 Spring Boot 中配置非常简单,只需要添加 @GlobalTransactional 注解即可:
@GlobalTransactional
public void placeOrder(OrderDTO order) {
inventoryService.deductStock(order.getProductId(), order.getCount());
logisticsService.reserve(order.getLogisticsInfo());
financeService.freeze(order.getAmount());
// 插入订单数据
orderMapper.insert(order);
}
Seata 会自动对各个服务的数据源进行代理,在本地事务提交前,会向 TC(Transaction Coordinator)注册分支事务,并插入 undo_log 回滚日志。
这样即使某个服务中途宕机,TC 也会协调其他参与者进行回滚,确保整体一致性。
异步操作:使用 RocketMQ 事务消息
对于一些允许短暂不一致的业务动作,比如触发通知、发送短信、更新缓存等,我们采用了 RocketMQ 的事务消息机制。
以“发送下单成功短信”为例,流程如下:
- 生产者发送 Half Message(半消息),消息进入 Broker 但不立即投递
- 生产者本地执行事务逻辑(比如写入数据库)
- 若执行成功,则提交事务,消息被正式投递给消费者
- 若失败,则回查事务状态,根据结果决定是否丢弃消息
这部分的关键在于本地事务和消息发送的绑定。我们在订单服务写完数据库后才提交事务消息,从而保证两者一致性。
数据核对:每日定时任务补偿
即便做了这么多努力,也不能完全避免偶发的消息丢失或者状态不一致的情况。因此我们还构建了一个“每日数据核对平台”。
这个平台每天凌晨跑一遍所有订单的状态,对比各子系统的数据,自动发起补偿动作:
- 比如订单已生成但未扣库存 → 主动调用库存服务补扣
- 已支付但资金未到账 → 查看交易流水重新记账
- 日志落盘以便追溯修复情况
通过这种方式,我们将“绝对一致”与“最终一致”结合起来,既保障了实时核心交易的稳定性,又通过异步手段兜底长尾问题。
效果总结:性能提升与稳定性增强
这套方案上线后,系统表现超出预期:
- TPS 提升约 2.5 倍:Seata 减少了跨系统调用的同步等待,同时消息异步解耦也释放了不少压力。
- 数据一致性显著提升:通过 SAGA + Seata + 消息事务的组合,关键业务路径实现了准实时一致性,日常异常率下降 90%。
- 运维复杂度可控:我们基于 Nacos 集中管理了事务配置,TC、RM、TM 的部署也都容器化,监控通过 SkyWalking 集成,整体可维护性较强。
- 故障恢复能力增强:当某一中心挂掉时,系统可以快速判断是否终止全局事务,或转入补偿流程,不会卡住整个流程。
当然也有代价,比如:
- 多了一层中间件依赖(Seata Server)
- 开发时需注意事务粒度控制,避免锁冲突
- 需要培训研发团队掌握新范式
不过总体来看,收益远大于成本。
经验分享与建议
如果你也在做类似的事情,想给你几点我踩过的坑、也吃过的糖:
1. 别迷信“一种方案打天下”
分布式事务没有银弹。Seata 也不是万能钥匙,有时候它反而会让你误以为“事务就是本地事务”,从而忽略性能瓶颈。
我见过有人把整段下单逻辑都包在 @GlobalTransactional 里,包括调用微信支付接口、查询外部地址库……这不仅违背了“快进快出”原则,还会造成 RM 持有连接太久,引发死锁和雪崩风险。
所以,要分清楚哪些是必须强一致性的环节,哪些可以容忍短时不一致。比如支付成功后触发优惠券发放的动作,完全可以放到事务外面异步执行。
2. 重视日志与监控体系的建设
分布式事务最大的痛苦不是出错,而是出了错不知道怎么查。
我们在早期没有做足够多的日志埋点,当一个订单出现异常时,只能靠人工翻数据库比对。后来我们做了两件事:
- 在每个事务分支中打印 UUID、branchId,方便串联链路;
- 使用 Zipkin + SkyWalking 做链路追踪,直观看到事务走向;
- 在日志中记录每一步的上下文参数(比如当前库存数量、冻结金额);
- 提供 UI 页面按订单 ID 快速查看事务状态树。
这些细节极大地提升了排查效率。
3. 补偿机制要有“兜底思维”
我在实际过程中吃过一次亏:一个订单的物流信息没推送成功,但财务那边已经冻结了资金。如果没有补偿机制,这笔钱就得人工干预,不仅效率低还易出错。
所以我们现在都会为每一个事务动作加上重试机制和补偿逻辑:
- 每条事务消息至少投递三次(支持指数退避);
- 每天跑核对脚本,找出异常订单并报警;
- 补偿动作具备幂等性,支持多次执行而不产生副作用;
- 关键路径设置熔断开关,必要时可切换为人工审核处理。
这些看似冗余的设计,其实在关键时刻救了我们几次命。
4. 技术趋势:拥抱云原生事务模型
最后还想提一下,近年来很多云厂商推出了自己的分布式事务产品(比如阿里云 GTS、华为云 DTM),封装得很好,可以降低不少学习成本。
如果你的团队资源有限,不想自行搭建 Seata 这类组件,也可以考虑这类服务。尤其是一些轻量级场景,比如只涉及两个系统之间的事务一致性,这些云服务往往是性价比更高的选择。
不过要注意,不要因为用了某些框架就觉得万事大吉。框架只是工具,背后的逻辑、设计思路才是真正的护城河。
写在最后:技术是服务业务的,别忘了初心

说到底,分布式事务的本质是一个“妥协的艺术”。你不可能追求完美的一致性,也不可能完全放弃一致性。你需要在性能、可用性、开发效率、维护成本之间找到那个“刚刚好”的平衡点。
在我参与的这个项目中,最初我们花了很多时间讨论理论模型,后来发现其实最重要的是:
- 清晰的业务边界划分
- 合理的事务编排
- 稳健的补偿策略
- 完善的可观测性
这些才是支撑一个稳定系统的基础。
希望这篇来自一线的分享对你有所启发。如果你有任何想法、疑问或者经历,欢迎留言交流。
愿你在每一次事务抉择中,都能走出稳稳的步伐。

评论 0