什么是分布式事务?为什么会产生?有哪些解决方案?
简化版
分布式事务指一个业务操作跨越多个数据库/服务,需要它们要么都成功、要么都失败(保持原子性)。它产生的根源是系统被拆分了——数据分散在多个库、业务分散在多个微服务,单机数据库的本地事务管不了跨库跨服务的一致性。解决方案分两类:强一致(2PC、3PC、XA)和最终一致(TCC、Saga、本地消息表、事务消息、最大努力通知)。互联网场景以最终一致方案为主。
详细版
为什么会产生分布式事务:
- 分库分表 / 多数据源:一个下单要写订单库 + 扣库存库,两个库的操作要一起成功。
- 微服务化:下单服务、库存服务、账户服务各是独立服务、各有自己的库,一次下单要跨服务保证一致。
单机事务靠数据库的 ACID(一个事务里 commit/rollback)就能保证原子性,但跨库、跨服务时没有一个统一的事务管理器天然存在,所以要额外的分布式事务方案。
主流方案对比:
| 方案 | 一致性 | 特点 | 适用 |
|---|---|---|---|
| 2PC / XA | 强一致 | 同步阻塞、性能差、有单点 | 传统金融、DB 层 |
| 3PC | 强一致 | 减少阻塞但更复杂,少用 | 理论为主 |
| TCC | 最终一致 | 业务侵入大、性能好、灵活 | 资金类高一致 |
| Saga | 最终一致 | 长事务、无锁、靠补偿 | 流程长的业务 |
| 本地消息表 | 最终一致 | 实现简单、依赖 MQ | 异步解耦场景 |
| 事务消息 | 最终一致 | MQ 保证、无本地表 | RocketMQ 场景 |
| 最大努力通知 | 最终一致 | 定时重试通知 | 对账/回调场景 |
完整版教学
一、从一个下单场景理解
用户下单:① 订单服务创建订单;② 库存服务扣减库存;③ 账户服务扣减余额。三个服务、三个库。要求「三者要么都成、要么都不成」——不能出现「订单创建了但库存没扣」(超卖)或「钱扣了但订单没生成」(用户投诉)。这就是分布式事务要解决的原子性问题。单机事务的 begin/commit/rollback 在这里失效,因为跨了三个独立的数据库连接、三个进程。
二、两条技术路线:刚性事务 vs 柔性事务
刚性事务(强一致,CP):追求任意时刻都一致,代表是 2PC/XA。用一个「事务协调者」统一指挥所有参与者先准备、再统一提交/回滚。优点是对业务透明、强一致;缺点是同步阻塞、锁资源占用久、协调者单点、性能差,不适合高并发互联网。
柔性事务(最终一致,AP/BASE):放弃「时刻一致」,允许中间短暂不一致,通过补偿、重试、对账保证最终一致。代表是 TCC、Saga、可靠消息。优点是高性能、高可用、不长时间占锁;缺点是业务侵入大、要处理各种边界(空回滚、悬挂、幂等)、编程复杂。互联网主流。
记忆点:分布式事务没有「银弹」,本质都是在「一致性强度」和「性能/可用性」之间取舍。强一致选 2PC/TCC,能接受最终一致就用消息/Saga 换取高吞吐。
三、各方案核心取舍
- 2PC:协调者两阶段——先让所有人「准备(锁资源)」,都 OK 再让所有人「提交」,有一个不 OK 就全「回滚」。
- 3PC:在 2PC 的准备前加一个「询问能否执行」阶段并引入超时,减少阻塞,但更复杂、实际少用。
- TCC:把每个操作拆成 Try(预留资源)、Confirm(确认扣除)、Cancel(释放预留)三个方法,业务自己实现,靠 Confirm/Cancel 补偿。
- Saga:把长事务拆成一串本地事务,每个配一个「反向补偿操作」,失败了就依次调用前面的补偿操作回滚。
- 本地消息表:业务操作和「发消息」记录写在同一个本地事务里,再由后台把消息投到 MQ,下游消费,失败重试,保证最终都做完。
- 事务消息:MQ(如 RocketMQ)提供「半消息」机制,本地事务成功才让消息真正投递,替代本地消息表。
- 最大努力通知:发起方尽最大努力(定时轮询、多次重试)通知接收方,接收方保证幂等,适合对账、支付回调。
四、如何选型
- 强一致、单体或传统架构、DB 支持 XA:2PC/XA(如跨库转账,Seata 的 XA 模式)。
- 资金类、要求较高一致性、能接受业务改造:TCC(如账户扣款)。
- 业务流程长、步骤多、可补偿:Saga(如旅游订票:机票+酒店+租车)。
- 异步解耦、允许最终一致:本地消息表 / 事务消息(如下单后加积分、发通知)。
- 对账、支付回调:最大努力通知。
实际大厂常用 Seata 框架,它统一封装了 AT(自动补偿,最常用)、TCC、Saga、XA 四种模式,按场景选。
五、分布式事务落地自检
判断一个业务是否需要分布式事务,可以先画出资源边界:一次请求写了几个库、几个服务、是否有 MQ、是否允许用户看到中间状态。比如下单主链路要求订单和库存最终一致,但积分发放可以延迟 10 秒,这就不必把所有动作塞进同一个强一致事务里。面试回答时把一致性等级拆开,才能体现不是无脑上 2PC 或 TCC。
六、常见误区与追问
这道题不能只背概念,要把「分布式事务总览」放回真实分布式系统里解释:谁发起、谁协调、状态如何变化、失败后怎么兜底,以及它和性能、可用性、一致性的取舍。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心结论 | 分布式事务解决跨库跨服务操作的一致性,方案分强一致 2PC/XA 和最终一致 TCC、Saga、消息等 | 不要停在名词解释 |
| 流程机制 | 业务拆分到多个服务 -> 本地事务无法覆盖全链路 -> 选择协调提交或补偿模型 -> 失败时重试或回滚 -> 通过幂等和对账收敛 | 说明触发方、参与方、状态变化和兜底 |
| 工程取舍 | 下单写订单、扣库存、扣余额跨 3 个服务,只成功 2 个就会出现业务不一致 | 分布式事务没有免费强一致,性能、可用性和业务侵入一定要取舍 |
分布式事务总览 面试拆解:
1. 业务拆分到多个服务
2. 本地事务无法覆盖全链路
3. 选择协调提交或补偿模型
4. 失败时重试或回滚
5. 通过幂等和对账收敛
记忆钩子:先说本地事务失效的原因,再拆协调、补偿、消息、幂等、对账和回滚边界;回答时要紧扣「分布式事务总览」这道题,不要把相邻概念混成一段泛泛的分布式套话。
- 误区:分布式事务能像本地事务一样便宜。 跨网络协调必然带来锁、延迟、可用性或业务侵入成本。
- 误区:所有场景都要强一致。 很多互联网场景接受最终一致,用消息和补偿换吞吐。
- 误区:用了框架就不用设计业务状态。 TCC、Saga、消息方案都要求业务幂等、补偿和状态机。
- 追问:2PC 和 TCC 最大区别是什么? 2PC 偏资源层协调,TCC 是业务层 Try/Confirm/Cancel。
- 追问:如何选型? 看一致性要求、响应时延、业务可补偿性和团队改造成本。
- 追问:最终一致怎么兜底? 可靠消息、重试、补偿任务、人工处理和定期对账。
七、加强记忆
分布式事务 = 跨库/跨服务的操作要一起成功或失败,根源是分库分表和微服务化。两条路线:刚性(强一致,2PC/3PC/XA,同步阻塞性能差)和柔性(最终一致,TCC/Saga/本地消息表/事务消息/最大努力通知,靠补偿重试对账)。互联网以柔性为主,核心是幂等+补偿。选型看一致性要求和业务能否改造,工程上常用 Seata 统一封装 AT/TCC/Saga/XA 四种模式。