分布式事务解决方案:实战经验分享
引言:为什么分布式事务是躲不开的坎儿

在我从事后端开发的五年里,遇到过各种各样的系统架构问题,但最让人头疼、最容易在生产环境中出问题的就是分布式事务。刚开始接触这个问题的时候,我也是一脸懵逼,看着业务逻辑明明没有问题,结果因为某个服务调用失败导致数据不一致,整个系统就像一台老旧的钟表,咔哒咔哒响却走不准时间。
我们做的是电商平台的订单系统重构项目,从单体应用拆分为微服务架构后,原本简单的下单流程变得复杂起来。用户付款之后,库存要减、积分要加、物流信息要创建、通知服务也要触发……这一系列操作分散在多个不同的服务中,每个服务又都有自己独立的数据库。如何保证这些操作要么全部成功,要么全部失败?这就引出了一个必须面对的问题——分布式事务的一致性保障。
这篇文章不是理论堆砌,而是我在项目实际推进过程中踩过的坑和总结出来的经验。希望能帮助你少走一些弯路。
项目背景与挑战:电商订单系统拆分带来的“一致性”危机

我们的电商平台最初是一个典型的单体架构,所有模块都跑在一个数据库上,事务管理靠数据库 ACID 特性就能解决。后来随着用户量增长和功能扩展,系统响应变慢,维护成本越来越高。于是公司决定进行服务化改造,将订单、支付、库存、会员、物流等模块拆分成各自独立的服务。
一开始大家还挺兴奋的,觉得服务拆开了,部署灵活了,运维也好做了。可上线没多久就出现了一个严重的问题:
用户付款之后,订单状态变成已支付,但库存没扣!
或者反过来:库存扣了,但订单没写入!
这种问题在单体架构下几乎不会出现,但在多服务间通信的情况下就成了家常便饭。特别是当调用链变长,中间任何一个环节失败都会导致整体事务不一致。
这时候我们就意识到,不能只靠本地事务来处理跨服务的数据变更了,得引入合适的分布式事务解决方案。
解决方案:技术选型与落地过程


第一步:理解需求和限制条件
我们团队在评估之前先明确了几点关键需求:
- 最终一致性即可,不需要强一致性(比如允许延迟几秒同步)
- 高并发环境下不能拖慢核心路径性能
- 需要有一定的容错能力
- 尽量减少对现有服务的改动
- 运维成本可控
基于这几个原则,我们开始调研主流的几种方案,并结合项目实际场景做出取舍。
方案一:两阶段提交(2PC)——理想很丰满,现实很骨感
起初我们也想过使用经典的 2PC 协议,这玩意儿在课本上讲得很优雅,理论上也能做到强一致性。不过实践下来才发现,它对系统的可用性和性能影响太大。
- 协调者单点故障会导致整个事务阻塞
- 多个资源服务器之间的网络通信容易成为瓶颈
- 高并发下性能下降明显,尤其在 DB 操作频繁的场景下
我们尝试在测试环境搭建了一套 Seata 的 AT 模式(本质也是类 2PC),发现一旦某个分支事务失败,整个事务就会回滚,而日志堆积太多还可能引发宕机。
结论:不适合我们这种对性能要求高的交易系统。果断放弃。
方案二:本地消息 + 最终一致性补偿机制——务实的选择
最终我们采用了更贴近实际业务的做法:本地事务消息表 + 异步补偿机制。
简单来说就是:
- 在本地事务中同时写业务数据和一个“事务消息”
- 然后有一个定时任务去扫描这些消息,按顺序调用其他服务完成剩余的操作
- 如果中途失败,会不断重试,直到成功或达到最大重试次数
以“下单-扣库存”为例:
@Transactional
public void createOrderAndReserveStock(Order order, Stock stock) {
// 1. 写订单
orderDao.insert(order);
// 2. 扣库存(更新库存记录)
int affected = stockDao.decreaseStock(stock.getProductId(), stock.getCount());
if (affected == 0) {
throw new RuntimeException("库存不足");
}
// 3. 发送本地事务消息到事务消息表
messageQueueService.sendMessage("order-created", order.getId());
}
然后,我们会有一个定时任务定期查询未处理的消息,并通过 RocketMQ 将其广播给下游服务(如积分服务、物流服务等)。如果某条消息发送失败,任务会在下一周期继续尝试。
这个方案有几个优点:
- 实现简单,不依赖额外的分布式事务框架
- 本地事务天然支持,数据一致性有保障
- 出现异常可以异步补偿,不影响主流程性能
- 可观测性强,可以通过消息表看每条事务的状态
当然缺点也很明显:
- 不保证强一致性,存在短暂的中间态
- 补偿逻辑需要自行实现
- 消息重复消费和幂等设计必须做好
我们在这个过程中也遇到了几个典型问题:
- 消息丢失:数据库插入成功,但消息没发出去。我们加了重试机制+失败报警。
- 消息重复消费:某些服务收到相同 ID 的订单,处理多次。我们在接口层加入了幂等处理,比如 Redis 去重或数据库唯一索引。
- 补偿任务积压:定时任务频率设置不合理,消息堆积严重。最后我们调整为异步消息队列触发 + 定时兜底策略。
方案三:TCC / Saga 模式 —— 适合金融类系统的重型武器
我们也研究过 TCC(Try-Confirm-Cancel)和 Saga 模式,这两种模式更适合金融、资金划转等对数据一致性要求非常高的场景。
以 TCC 为例:
- Try:资源预留(冻结库存)
- Confirm:执行业务动作(真正扣库存)
- Cancel:释放资源(解冻库存)
这套机制对代码侵入性强,每一步都需要定义对应的补偿方法,开发量大,但能更好地控制事务边界。
我们在一次结算系统升级中尝试使用过 TCC 框架(如 ByteTCC),虽然功能强大,但也带来了很多负担,例如:
- 接口定义变得更复杂
- 测试和调试成本显著增加
- 幂等、并发控制要考虑更多边界情况
对于我们当前的项目来说,还是选择了更轻量的方案。
实施效果:稳定性提升明显


引入本地消息+补偿机制后,整个系统的稳定性有了明显提升:
| 指标 | 改造前 | 改造后 |
|---|---|---|
| 数据不一致率 | ~0.3% | <0.001% |
| 订单处理耗时 | 平均 380ms | 平均 190ms |
| 错误日志数/天 | 500+ 条 | 10~20 条 |
| 手动干预次数 | 每周约 1~2 次 | 基本无需干预 |
而且由于把一部分工作异步化,核心下单路径不再被多个服务调用阻塞,性能也有明显提升。
最重要的是,系统具备了较强的容灾能力。即使某个下游服务挂掉一段时间,也不会影响主流程,只要服务恢复后补偿任务自动补全即可。
经验分享:避免踩坑的关键建议
如果你现在正面临分布式事务相关的问题,以下是我这几年的经验总结:
✅ 1. 优先考虑业务自身设计是否能规避分布式事务
有些时候,数据耦合其实是设计上的问题。比如你可以:
- 合并服务边界,将高耦合的操作放在同一个服务内
- 使用冗余字段减少跨服务依赖(如订单中直接存商品名、价格)
- 将非实时需求异步化处理,降低事务跨度
✅ 2. 选择方案要看场景,不要盲目追求强一致性
分布式事务有很多种方案,每种都有适用场景:
- 强一致性要求高的金融类系统:TCC、Seata(AT/SAGA)
- 性能要求高的交易类系统:本地事务表+补偿+消息队列
- 日志、通知等弱一致性需求:完全异步 + 事件驱动
✅ 3. 幂等设计是王道,一定要提前做好
不管是消息消费、RPC 调用,还是 HTTP 请求,幂等设计必不可少。常见的做法有:
- 用唯一 ID 结合 Redis 缓存判重
- 数据库层面加唯一约束或版本号
- 每次请求带上 sequence ID 用于去重
我们曾经就因为没有做幂等,导致一次消息重复消费使用户的积分增加了好几次,差点被投诉。
✅ 4. 监控体系很重要,异常要及时发现和预警
我们后来在消息平台加上了监控大盘:
- 消息堆积数量
- 消费失败率
- 每分钟消息吞吐量
- 整体成功率趋势图
同时接入告警系统,一旦超过阈值立刻通知值班人员。这些细节做得好,真的能救命。
✅ 5. 技术只是辅助,流程和规范才是保障
再好的技术方案也离不开合理的开发规范。我们在项目推进中制定了几个制度:
- 每个新增的跨服务操作必须提供补偿方案
- 接口中必须声明是否幂等
- 重大数据修改必须记录操作日志,方便后续排查
这些看似“土味”的规定,其实对长期维护帮助极大。
技术趋势:未来可能会怎么演进?
目前来看,业界在分布式事务方面也在不断演进,比如:
- Event Sourcing + CQRS 架构逐渐流行,通过事件流的方式来做事务状态追踪
- Serverless 与 Event-driven 架构结合,使得异步补偿更简单高效
- 中台概念推动下,越来越多公司将关键资源集中管理,一定程度缓解了分布问题
我个人也在关注一些新兴技术,比如 Dapr 提供的 Distributed Application Runtime,在统一抽象下简化了服务间通信和事务处理。虽然还不够成熟,但在中小规模系统中已经值得一试。
结语:分布式事务不是万灵药,关键是找到平衡
回过头看整个项目的推进过程,最大的体会是:
分布式事务不是用来解决一切问题的银弹,它只是众多工具中的一个。能否解决问题,还得看你是不是用对了地方。
技术方案没有绝对的好坏,只有适合与否。希望我的这段经历,能对你有所帮助。如果你正在做类似的事情,不妨停下来想一想:
- 我们真的需要严格的分布式事务吗?
- 是否可以通过业务设计来规避跨服务问题?
- 如果必须做,那么我们能接受哪些代价?
这些问题想明白了,技术选型才会更有方向,而不是为了“高大上”而选型。
如有任何问题或交流想法,欢迎留言讨论~

评论 0