Skip to content

分布式事务解决方案(总纲) ​

本篇是「分布式理论」目录下分布式事务系列的总纲。先用一张全景对比表建立整体认知。再逐个过一遍每种方案的流程与优缺点。最后给出选型思路。各方案的深度细节拆在单篇里讲,文末「延伸阅读」有完整索引。

前置知识:分布式事务产生于跨库、跨服务、跨进程的场景(微服务间远程调用、单体访问多库等)。本质是把单机 ACID 事务的概念扩大到多个数据源。目的是保证分布式环境下的数据一致性。概念不清楚的先看 什么是分布式事务?。

一、先建立坐标系:刚性事务与柔性事务 ​

所有分布式事务方案,本质上都是在 CAP 里做取舍,可以分成两大阵营:

  • 刚性事务:遵循 ACID,追求强一致性,事务过程中资源被锁定、参与者同步等待。代表:2PC/XA、3PC。性能差,但一致性强。

  • 柔性事务:遵循 BASE 理论,放弃强一致,追求最终一致性。允许中间状态存在短暂的数据不一致,通过补偿、重试、对账这类手段兜底。代表:TCC、本地消息表、事务消息、Saga、最大努力通知。互联网高并发场景的主流选择。

理论基础见 分布式理论-CAP原则 和 分布式理论-BASE,分类概念见 刚性事务与柔性事务。

二、全景对比表 ​

方案一致性性能复杂度适用场景
2PC(XA)强一致低(同步阻塞、锁资源)低(数据库层实现)单应用跨多库、低并发、强一致要求
3PC强一致(仍有不一致风险)较低(多了一轮交互)中理论改进,工程上很少直接使用
TCC最终一致(业务层面准强一致)较高(一阶段提交本地事务,不锁数据库资源)高(每个参与者要写 Try/Confirm/Cancel 三个接口)一致性敏感且并发高的核心场景(如资金、支付、交易)
本地消息表最终一致中(消息与业务耦合在同一库)中中小系统、并发不高的异步解耦场景
事务消息(RocketMQ)最终一致高(异步、无本地消息表)中(依赖 MQ 的事务消息能力)高并发的异步场景,国内互联网主流方案
Saga最终一致高(一阶段直接提交本地事务,无锁)中高(需编写正向服务和补偿服务)业务流程长、参与者多、包含第三方/遗留系统(无法改造出 TCC 三接口)的长事务
最大努力通知弱(不保证一定送达)高低对一致性要求最低的通知类场景,如支付结果通知商户

几个速记结论:

  • 一致性强度:2PC ≈ 3PC > TCC > 本地消息表 ≈ 事务消息 ≈ Saga > 最大努力通知。

  • 性能:反过来看,越「柔」越快,柔性事务普遍比刚性事务高一个量级。

  • 复杂度的转移:刚性事务把复杂度交给数据库和中间件(应用几乎不用改代码)。柔性事务把复杂度还给了业务(要写补偿、要处理幂等和空回滚)。

三、各方案流程与优缺点 ​

1、2PC(二阶段提交 / XA 方案) ​

流程:由协调者(事务管理器 TM)和参与者(资源管理器 RM)配合完成。第一阶段(投票/准备):协调者询问所有参与者能否提交。参与者执行事务并写好 Undo/Redo 日志但不提交,回答 Yes/No。第二阶段(执行/提交):只要有一个参与者回答 No 或超时,协调者向所有人发 Rollback。否则发 Commit,参与者执行后释放资源并回 Ack。

优点:原理简单,数据库层面原生支持(XA 协议),应用几乎零改造,强一致。

缺点:同步阻塞(所有节点锁着资源等协调者指令,性能极差)。协调者单点。极端情况下(Commit 指令只发到一部分参与者)数据不一致。容错性差,任一节点故障拖垮全局。绝对不适合高并发场景,微服务规范下「一个服务只操作自己的库」,跨库直连本身就不合规。

详细拆解见 分布式理论-2PC,DTP 模型与接口规范见 XA协议,术语见 全局事务与分支事务。

2、3PC(三阶段提交) ​

流程:在 2PC 的准备与提交之间插入一个阶段。变成 CanCommit(询问,不锁资源)→ PreCommit(预提交,写 Undo/Redo 日志)→ Do Commit(真正提交)。并且协调者和参与者都引入了超时机制:参与者没有等到 doCommit 指令时,超时默认提交。

优点:相对 2PC 解决了协调者单点导致的永久阻塞(参与者超时后自行决策,减少了阻塞时间)。也缓解了同步阻塞。

缺点:仍然存在数据不一致——网络异常导致 abort 请求没送到。参与者超时后自动 commit,就和其他回滚的参与者不一致。多一轮网络交互,延迟更高。工程上很少直接落地,更多是为理解一致性协议演进服务。

详细拆解见 分布式理论-3PC。

3、TCC(Try-Confirm-Cancel) ​

流程:把事务的控制权从数据库层上移到业务层,每个参与者实现三个接口:

  • Try:检查并预留/冻结资源(如冻结订单金额、预扣库存),不执行真正业务;

  • Confirm:全部 Try 成功后执行,用预留资源完成真正的业务操作(扣款、扣库存),要求幂等;

  • Cancel:任一 Try 失败后执行,释放预留的资源(解冻),同样要求幂等。

优点:一阶段就提交了本地事务、不长期锁数据库资源,性能比 XA 好得多。一致性接近强一致(Try 成功后 Confirm 基本必成),适合资金类核心场景。

缺点:业务侵入大,每个参与者都要开发三个接口,补偿/回滚代码量大、难维护。要自行处理三个经典坑。一是空回滚:Cancel 先于 Try 到达,需判断 Try 是否执行过。二是幂等:Confirm/Cancel 可能重试多次。三是悬挂:Cancel 执行后迟到的 Try 才到达,资源被白白冻结。网络连不通时的处理是面试高频追问点。

4、本地消息表 ​

流程(eBay 提出的经典方案):

1)A 系统在同一个本地事务里完成业务操作。并向本地消息表插入一条消息记录(业务与消息原子绑定);

2)A 系统将消息发送到 MQ;

3)B 系统收到消息后,在本地事务里执行业务。并向自己的消息表插入记录(靠唯一约束防重复消费);

4)B 成功后更新两侧消息表状态;

5)A 定时扫描本地消息表中未确认的消息,重新投递,直到 B 成功为止。

优点:思路简单可靠,不依赖 MQ 的事务特性,最终一致性有保证。

缺点:消息表与业务耦合在同一个库,高并发下数据库成为瓶颈、难扩展。定时扫表有延迟。一般只用于并发不高的中小系统。

5、事务消息(可靠消息最终一致性方案) ​

流程(以 RocketMQ 事务消息为例):

1)A 先发一条 half(prepared)消息到 MQ,发送失败则直接终止;

2)half 消息成功后,A 执行本地事务;成功则向 MQ 提交确认,失败则回滚消息;

3)MQ 收到确认后才把消息投递给 B,B 消费并执行自己的本地事务;

4)回查兜底:MQ 定时轮询长时间未确认的 half 消息。回调 A 的接口询问本地事务到底成没成功,A 查库后答复提交或回滚。避免「本地事务成功、确认消息发送失败」导致消息丢失;

5)B 失败则自动重试,直到成功。资金类等重要业务可触发反向回滚或人工介入。

优点:去掉了本地消息表,业务与消息解耦,性能好,是目前国内互联网公司最主流的方案。

缺点:依赖 MQ 的事务消息能力(RocketMQ 原生支持,其他 MQ 需自己封装类似逻辑)。消费方必须幂等。只能保证最终一致,中间态可被感知。

6、Saga ​

流程:把长事务拆成 T1、T2、…、Tn 一串本地事务,每个都直接提交(无锁、高性能)。若执行到 Tk 失败,则反向执行补偿服务 Ck-1、…、C2、C1,把之前已提交的修改逐一补偿掉。两种实现:编排(orchestration,中央协调器驱动)与协同(choreography,事件驱动)。

优点:一阶段提交本地事务、无锁、高性能。参与者可异步执行、高吞吐。补偿服务好写(一次更新操作的反向操作通常容易理解)。参与者可以是第三方或遗留系统——它们只需要提供正向+补偿接口,不必满足 TCC 的三接口要求。

缺点:不保证隔离性,中间状态对外可见(可能出现脏读、已提交的数据被补偿回滚)。补偿逻辑本身要业务正确。一环失败波及整条补偿链。

适用:业务流程长、流程多、跨公司/遗留系统的长事务(金融渠道层、产品层、系统集成层)。是业界公认的长事务解决方案。Seata 的 Saga 模式即源于此。

7、最大努力通知 ​

流程:A 执行完本地事务后发消息到 MQ,由专门的通知服务消费并调用 B 的接口。B 失败则按衰减间隔(如 1min/5min/10min/30min/1h…)重试 N 次,仍失败就放弃。同时提供查询接口供 B 主动对账,必要时人工介入。

优点:实现最简单,成本最低。

缺点:一致性最弱,不保证最终一定送达,需要 B 侧配合对账兜底。

适用:一致性要求最低的通知类场景,典型如支付宝回调通知商户支付结果、短信/邮件通知。

四、怎么选(面试话术) ​

面试被问「你们公司怎么处理分布式事务」,可以按这个层次回答:

1)先讲取舍:并发高的互联网场景很少碰 XA,主用柔性事务保最终一致;

2)资金/交易等强一致 + 高并发的核心场景:TCC。严格保证要么全成功要么自动回滚;

3)一般场景(如下单后异步扣库存):事务消息/可靠消息最终一致性(RocketMQ);

4)长流程、长事务、含第三方系统:Saga 补偿;

5)纯通知类:最大努力通知;

6)再补一句共同前提:无论哪种柔性方案,幂等、重试、对账、人工兜底都是标配。

工程上还可提及 Seata:AT 模式(无侵入的自动补偿型 2PC 思路)、TCC、Saga 模式一框架多模式。原理与本篇一一对应。

五、延伸阅读(同目录系列篇) ​

2026-10 依据主题重写(原文件为空壳)。