什么情况下需要使用分布式事务,有哪些方案?
- 标签:Spring Cloud、微服务、分布式事务、后端
🏛 业务架构师
先回答"什么时候需要":跨服务/跨库的写操作,且中间状态对业务不可接受才需要分布式事务。我的第一反应永远是反问——能不能靠"同库事务 + 幂等 + 对账"绕开?绕不开的才上方案,因为每个方案的价码都不便宜:
- 2PC/XA:强一致,但同步阻塞、协调者单点、性能差,只留给数据库层面的低频强一致操作(资金调拨),高并发交易不用;
- TCC(Try-Confirm-Cancel):业务层面的两阶段,性能好、一致性强,但每个参与方要实现三套逻辑,开发成本翻倍,且要处理空回滚、悬挂、幂等三类经典问题。留给资金核心链路:冻结-扣减-解冻的余额操作就是教科书场景;
- SAGA:长事务拆成本地事务序列 + 逆向补偿。适合流程长、参与方多、允许补偿的场景——订机票+酒店+租车,退订就是逆向补偿。代价是补偿逻辑等于再写一遍业务;
- 本地消息表 / 事务消息(RocketMQ):最终一致,成本最低,互联网业务的主力选择——下单送积分,秒级延迟完全可接受;
- 最大努力通知:对外部第三方(回调重试+查询补偿),配合对账。
我的决策树:能同库就同库;能最终一致就上消息+幂等;资金级强一致才 TCC;跨企业协调用 SAGA+对账。最后强调:所有方案的兜底都是对账——分布式事务方案降低不一致概率,对账负责抓住漏网之鱼并修复,两者是搭档不是替代。
🏗 系统架构师
先说判断标准:跨库/跨服务的数据变更,本地事务罩不住了,才需要分布式事务。典型触发点:下单服务扣库存服务的量、转账跨行、订单+积分+券跨服务落库。能用本地事务合并的(同库)、能靠最终一致补偿的,我都不会上强一致方案——这是成本判断。
方案谱系(按一致性强度与性能排):
- 2PC/XA:强一致,协调者阻塞、单点、性能差(同步阻塞 + 锁持有久),只用于极少数金融核心、低频大额;
- TCC(Try-Confirm-Cancel):业务层面两阶段,Try 预留资源、Confirm/Cancel 由业务实现,性能好于 XA 但侵入大(每个参与方三个接口 + 幂等 + 空回滚 + 悬挂处理),适合资金类核心链路;
- Seata AT:自动生成反向 SQL 补偿,开发几乎无感,靠全局锁防脏写,性能中等,是微服务里的平衡选择;
- Saga:长事务拆本地事务 + 逆向补偿,无全局锁吞吐高,只保证最终一致,适合跨企业、流程长的场景;
- 本地消息表 / 事务消息 + 重试 + 幂等:最大努力最终一致,我的默认选择,简单可靠可观测。
我的选型口诀:钱强一致用 TCC,一般业务用 AT 或消息驱动最终一致,2PC 留给低频金融核心。
💻 全栈开发专家
什么时候需要:一个操作跨多个库/多个服务,本地事务管不住——比如下单扣 A 服务的库存、写 B 服务的订单、加 C 服务积分。
方案谱系(按一致性强度排):
- 2PC/XA:数据库原生强一致,同步阻塞、性能差,我实际只在同实例 DB 的强一致小范围用过,互联网高并发场景基本排除。
- Seata AT:无侵入(代理数据源、undo_log 反向补偿),两阶段异步,适合快速接管的存量系统;代价是全局锁竞争、seata-server 要高可用。
- TCC:Try(预留)/Confirm(确认)/Cancel(释放),业务侵入大、每个参与者写三个接口,但性能好、可控性强——资金/库存这类核心场景我用它。
- 本地消息表 / 事务消息(最终一致,我的默认选择):本地事务 + 消息 + 下游幂等消费 + 对账兜底,实现简单、可观测、失败可补偿。绝大多数"跨服务数据同步"场景它就够。
java
// 本地消息表核心:业务与消息记录同事务
@Transactional
public void createOrder(Order o) {
orderMapper.insert(o);
messageLogMapper.insert(new MessageLog("ORDER_CREATED", o.toJson(), PENDING)); // 同库同事务
}
// 定时任务扫 PENDING 发 MQ,成功标记 DONE,失败重试选型话术:优先问"业务能不能接受秒级不一致",能就本地消息表;不能且是资金,TCC;尽量别碰 XA。任何方案最后都要有对账,这是工程上的最后一道保险。