做微服务的时间久了,你迟早会遇到一个叫“分布式事务”的坎。我印象最深的一次,是凌晨两点被电话叫醒,说线上订单和库存对不上。查了半天,订单创建成功了,库存扣减却在远程调用超时后被回滚了,用户的账单上留下了一笔“幽灵订单”。从那以后我就明白了,微服务架构里,分布式事务不是一个“要不要学”的技术问题,而是一个“迟早得面对”的业务问题。
这篇文章我会结合实际的项目实践,把分布式事务的几个主流方案——2PC、TCC、SAGA、本地消息表、MQ事务消息——的原理、代价和选型思路都拆开讲清楚。既适合正在做微服务改造的开发者,也适合准备面试、想把这块讲明白的人。核心不是说哪个方案最好,而是帮你建立一套判断逻辑:在什么场景下,选什么方案,付出的代价是什么。
1. 从订单扣库存说起:分布式事务到底在解决什么
1.1 一个本地事务为什么管不住跨服务操作
先看一个最经典的场景:下单。在单体应用时代,订单表和库存表在同一张数据库里,你只需要开一个本地事务,扣库存、建订单、加流水,要么全成功要么全失败,ACID四个特性由数据库帮你兜底。
微服务拆分以后,订单服务有订单库,库存服务有库存库。一次下单操作,本质上是“订单服务写库 + 远程调用库存服务扣减”。传统本地事务的边界只在自己那个数据库内部,根本管不住这次跨服务、跨库的调用链。如果订单库提交成功、库存服务因为网络超时没扣成,两边数据就永远对不上了。
这里有个关键认知:分布式事务的本质,不是“把分布式操作变成本地事务”,而是“在无法保证原子性的前提下,通过一系列机制让数据最终达成一致”。它处理的核心矛盾是:网络不可靠、服务可能宕机、数据无法一把锁住。搞清楚了这一点,你再看各种方案,思路会清晰很多。
1.2 分布式事务的本质是一个“一致性取舍题”
很多人一上来就纠结“用哪个框架”,其实第一步应该想清楚:你的业务能接受多大的不一致窗口。
- 金融转账、支付扣款,这种场景用户对金额高度敏感,连“短暂不一致”都不太能忍。
- 订单创建、积分赠送、发短信通知,这种场景稍微延迟几秒甚至几分钟,用户基本感知不到。
- 搜索索引更新、缓存刷新、报表统计,这种场景根本不需要强一致,异步做掉就行。
分布式事务的各种方案,本质上就是在一致性强度、可用性、性能、业务侵入性这四个维度之间做取舍。没有免费的午餐,你选了强一致,就要接受性能损失和实现复杂度;你选了最终一致,就得在中途状态可被观测、需补偿的方向上做文章。
我给很多团队做过技术评审,最常见的分歧就是:产品经理坚持“数据必须完全一致”,而研发觉得“最终一致就行了”。这种分歧不解决,方案选型就是空中楼阁。所以第一步一定先把业务对一致性的容忍度聊透。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 主流分布式事务方案的原理拆解与真实代价
2.1 2PC/XA/AT:强一致但代价不低
两阶段提交(2PC)是分布式事务最经典的实现思路,XA协议就是它的数据库标准实现。整个过程分两步:
- 准备阶段:协调者问所有参与者“能不能提交”,各参与者执行事务但先不提交,而是写日志、锁资源,然后回复“OK”。
- 提交阶段:协调者收到所有“OK”后,广播提交指令,各参与者正式提交;只要有一个参与者说“不行”,协调者就广播回滚。
听起来逻辑很严谨,但2PC有个著名的软肋:阻塞。准备阶段参与者持有的数据库锁不会释放,一旦协调者宕机,参与者就只能一直等下去。这也是为什么传统XA方案在互联网高并发场景下很难铺开的原因——它不是不能保证一致性,而是为了保证一致性把系统吞吐锁死了。
后来Seata的AT模式在XA思路上做了优化,引入了全局锁和undo_log。它通过代理数据源自动记录操作前后镜像,提交时生成undo_log,回滚时根据undo_log反向补偿。对业务代码侵入非常小,一个@GlobalTransactional注解就能用起来。但它本质上还是2PC思想,换来的是性能损耗——本地事务提交前需要检查全局锁,高竞争场景下锁等待会很酸爽。
2.2 TCC:业务侵入性强但性能好
TCC是最“硬核”的一个方案,它把每个操作拆成三个业务
