news 2026/9/18 5:46:29

分布式事务完全指南:XA、TCC、Saga、本地消息表与可靠消息最终一致性方案对比(doocs/advanced-java)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
分布式事务完全指南:XA、TCC、Saga、本地消息表与可靠消息最终一致性方案对比(doocs/advanced-java)

分布式事务完全指南:XA、TCC、Saga、本地消息表与可靠消息最终一致性方案对比(doocs/advanced-java)

【免费下载链接】advanced-java😮 Core Interview Questions & Answers For Experienced Java(Backend) Developers | 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识项目地址: https://gitcode.com/doocs/advanced-java

分布式事务是微服务与分布式系统面试中的必考话题。本文基于 doocs/advanced-java 仓库的分布式事务原理一文,系统梳理业界主流的 6 种分布式事务方案——XA 两阶段提交、TCC、Saga、本地消息表、可靠消息最终一致性、最大努力通知——逐一剖析其核心原理、执行流程、适用场景与典型坑点,并结合仓库中消息可靠性、消息幂等性、接口幂等性、CAP 定理等关联文档,讲清"强一致性方案"与"最终一致性方案"的选型逻辑,让你在面试与真实架构设计中都能给出有据可依的答案。

为什么面试必问分布式事务

只要聊到分布式系统,分布式事务就是绕不开的标配问题。你在单机单体架构下使用数据库本地事务即可保证一致性,但一旦系统拆分成几十甚至上百个微服务、数据分散到多个数据库,一次业务操作往往会跨多个服务、跨多个库执行,"要么全部成功、要么全部回滚"的本地事务语义就失效了,这就必须引入分布式事务。

面试官考察这一点的核心意图是:你起码得知道有哪些方案,一般怎么来做,每个方案的优缺点是什么。你不一定真正在生产中落地过,但必须能清晰地说出备选方案的种类、各自的坑点——比如 TCC 方案的网络问题、XA 方案的一致性问题——这正是本文要解决的核心问题。

从仓库的分布式系统章节索引可以看到,分布式事务与分布式锁、分布式会话、幂等性、请求顺序性共同构成了分布式系统的核心难点,而事务又和MQ 面试连环炮里"如何保证消息不丢、不重复、有序"等问题深度耦合。

分布式事务实现的 6 种主流方案

目前业界实现分布式事务主要有以下 6 种方案:

  • XA 方案(两阶段提交)
  • TCC 方案(Try / Confirm / Cancel)
  • SAGA 方案(长事务补偿)
  • 本地消息表
  • 可靠消息最终一致性方案
  • 最大努力通知方案

前三种偏"强一致性 / 业务补偿"路线,后三种偏"最终一致性 / 消息驱动"路线。下面逐一展开。

XA 方案:两阶段提交

核心原理

所谓的 XA 方案,即两阶段提交(2PC),核心是引入一个事务管理器(Transaction Manager),负责协调多个数据库(资源管理器,Resource Manager)的事务:

  1. 第一阶段(准备 / Prepare):事务管理器先询问各个数据库"你准备好了吗?";
  2. 第二阶段(提交 / Commit):如果每个数据库都回复 ok,就正式提交事务,在各个数据库上执行操作;如果任何一个数据库回答不 ok,那么就回滚事务。

从图中可以看到:一个系统内部持有事务管理器,事务管理器分别连接数据库 1、2、3,第一阶段"询问"、第二阶段"执行",正是典型的集中式两阶段提交协调模型。

适用场景与局限

这种方案比较适合单块应用里跨多个库的分布式事务,可以基于Spring + JTA实现(自行搜索 demo 即可了解用法)。但它有两个致命问题:

  • 效率低:严重依赖数据库层面来搞定复杂的事务,绝对不适合高并发的场景;
  • 规范冲突:微服务架构下,一个大的系统会拆成几十个甚至几百个服务。规范要求每个服务只能操作自己对应的一个数据库;如果允许交叉访问别人的库,几百个服务全体乱套,数据可能被别人改错、自己的库被别人写挂,整个服务无法管理和治理。

因此正确做法是:要操作别的服务的库,必须通过调用别的服务的接口实现,绝对不允许交叉访问别人的数据库。也正因如此,XA 方案在实际工程中很少用——某个系统内部如果出现跨多个库的操作,本身就属于不合规的设计。

相关延伸:分布式系统为什么必须考虑一致性取舍,可参考仓库中 CAP 定理 P 的含义一文——XA 属于典型的强一致性(CP 倾向)路线,以牺牲可用性和性能换取数据一致。

TCC 方案:Try / Confirm / Cancel

TCC 的全称是TryConfirmCancel三个阶段:

  • Try 阶段:对各个服务的资源做检测,并对资源进行锁定或者预留。典型操作如"冻结资金";
  • Confirm 阶段:在各个服务中执行实际的操作。Try 阶段所有资源预留成功后,进入 Confirm 阶段真正扣款、转账、落库;
  • Cancel 阶段:如果任何一个服务的业务方法执行出错,就需要进行补偿——把那些已经执行成功的业务逻辑回滚掉。

图中示例是一个典型的资金场景:用户发起操作后,系统 A 在 Try 阶段冻结银行 B、银行 C 的资源,Confirm 阶段执行插入、扣款、转账,一旦失败则进入 Cancel 阶段回滚已执行的操作。

优劣势与适用场景

  • 优势:事务回滚是"自动"协调的,不依赖数据库分布式事务,能严格保证分布式事务要么全部成功、要么全部自动回滚;
  • 劣势:事务回滚严重依赖于你自己写代码来回滚和补偿,会造成补偿代码巨大、非常难以维护。

因此 TCC 的实际使用场景非常聚焦:

  • 相关的、支付交易相关场景,严格保证资金的正确性,资金上不能出任何问题;
  • 最好是各个业务执行的时间都比较短,避免长时间占用预留资源。

但说实话,一般尽量别这么搞,自己手写回滚/补偿逻辑实在太恶心,业务代码很难维护。TCC 最常见的坑是网络问题:Try 成功了但 Confirm 请求因网络异常丢失、或 Cancel 补偿请求丢失,都需要靠超时重试、幂等接口来兜底,这正是面试官常问的"TCC 网络连不通怎么办"。

Saga 方案:长事务的补偿模式

基本原理

金融核心等业务追求强一致性和更高并发量,会选择 TCC;而金融核心以上的更多业务系统,往往选择补偿事务(Compensating Transaction)。补偿事务处理在 30 多年前就提出了 Saga 理论,随着微服务的发展近些年才逐步受到关注。目前业界比较公认的是采用 Saga 作为长事务的解决方案

Saga 的基本原理是:业务流程中每个参与者都提交本地事务,若某一个参与者失败,则补偿前面已经成功的参与者

如上图所示:左侧是正常事务流程,依次执行 T1、T2、T3……;当执行到 T3 时发生错误,则开始执行右侧的事务补偿流程,反向执行 T3、T2、T1 的补偿服务 C3、C2、C1,将 T3、T2、T1 已经修改的数据补偿掉。

适用场景

对于一致性要求高、短流程、并发高的场景(如金融核心系统),优先考虑 TCC 方案。而在另一些场景下,并不需要这么强的一致性,只需要保证最终一致性即可:

  • 很多金融核心以上的业务(渠道层、产品层、系统集成层),特点是最终一致即可、流程多、流程长,还可能要调用其它公司的服务;
  • 这类场景如果选 TCC:一来开发成本高,二来无法要求其它公司的服务也遵循 TCC 模式(提供 Try/Confirm/Cancel 三个接口),同时流程长、事务边界太长、加锁时间长,会严重影响并发性能。

因此 Saga 模式的适用场景可以归纳为:

  • 业务流程长、业务流程多;
  • 参与者包含其它公司或遗留系统服务,无法提供 TCC 模式要求的三个接口。

优势与缺点

优势

  • 一阶段提交本地事务,无锁,高性能
  • 参与者可异步执行,高吞吐
  • 补偿服务易于实现——一个更新操作的反向操作(比如"扣减库存"的反向是"回补库存")是比较容易理解的。

缺点

  • 不保证事务的隔离性。Saga 的各本地事务是逐步提交的,中间状态对其它事务可见,需要业务侧自行处理脏读问题。

本地消息表方案

本地消息表是国外 eBay 搞出来的一套思想,核心是"用本地事务保证消息的可靠性,用消息驱动对端异步执行"。

整体流程如下:

  1. A 系统在自己本地一个事务里操作业务数据的同时,插入一条数据到消息表;
  2. 接着 A 系统将这个消息发送到 MQ 中去;
  3. B 系统接收到消息之后,在一个事务里,往自己本地消息表里插入一条数据,同时执行其他的业务操作;如果这个消息已经被处理过了,那么这个事务会回滚,从而保证不会重复处理消息
  4. B 系统执行成功之后,更新自己本地消息表的状态以及 A 系统消息表的状态;
  5. 如果 B 系统处理失败了,那么就不更新消息表状态;此时 A 系统会定时扫描自己的消息表,发现未处理的消息会再次发送到 MQ 中去,让 B 再次处理;
  6. 这个方案保证了最终一致性:哪怕 B 事务失败,A 会不断重发消息,直到 B 那边成功为止。

从图中可以看到完整的闭环:A 系统的数据库里维护"业务表 + 消息表",A 通过 MQ 把消息发给 B,B 同样以"本地事务 + 消息表(带 orderId 唯一约束)+ 幂等判断"的方式消费,最后通过状态回写与定时扫描保证消息最终都被处理。

局限

这个方案最大的问题在于严重依赖于数据库的消息表来管理事务:高并发场景怎么办?怎么扩展?消息表本身会成为数据库的瓶颈。所以一般确实很少用。

相关延伸:B 系统"消息已处理过则回滚"依赖的就是消息消费幂等性,具体做法(数据库唯一键、Redis 记录消费 ID 等)可参考仓库的如何保证消息不被重复消费一文。

可靠消息最终一致性方案

这个方案的意思是:干脆不要用本地的消息表了,直接基于 MQ 来实现事务。比如阿里的 RocketMQ 就支持消息事务。

整体思路如下:

  1. A 系统先发送一个prepared 消息到 MQ,如果这个 prepared 消息发送失败,就直接取消操作别执行了;
  2. 如果消息发送成功了,接着执行本地事务:成功就告诉 MQ 发送确认消息,失败就告诉 MQ回滚消息
  3. 如果发送了确认消息,B 系统会接收到确认消息,然后执行本地的事务;
  4. MQ 会自动定时轮询所有 prepared 消息、回调你的接口,问你这个消息是不是本地事务处理失败了——所有没发送确认的消息,是继续重试还是回滚?一般来说这里可以查数据库看之前本地事务是否执行:如果回滚了,这里也回滚。这一步是为了避免"本地事务执行成功了,但确认消息却发送失败了"这种情况;
  5. 要是 B 系统的事务失败了怎么办?重试,自动不断重试直到成功;如果实在不行,要么针对重要的资金类业务进行回滚(B 系统本地回滚后想办法通知 A 也回滚),要么发送报警由人工来手工回滚和补偿
  6. 这个方案目前比较主流,国内互联网公司大都是这么玩儿的:要么直接用 RocketMQ 支持的事务消息,要么自己基于 ActiveMQ、RabbitMQ 封装一套类似的逻辑出来。

从图中可以看到:A 系统先发 prepared 消息,本地事务成功后发 confirm 消息,MQ 定时轮询未确认消息,B 系统消费消息执行本地事务——这套流程把"本地事务与消息发送的原子性"问题交给了 MQ 的确认与轮询机制来解决。

与本地消息表的本质区别

  • 本地消息表把"消息记录"放在业务库的同库消息表里,靠本地事务保证不丢,靠定时扫描重发;
  • 可靠消息方案把"未确认消息"托管给MQ 中间件,由 MQ 负责持久化、定时轮询与状态回调,业务系统无需维护消息表,扩展性更好。

相关延伸:方案中反复强调的"重试直到成功"依赖 MQ 的可靠性传输保障,具体到 RabbitMQ 的 confirm/事务机制、Kafka 的 acks 与副本机制,可参考仓库的如何保证消息的可靠性传输一文;而"B 系统重试处理、避免重复生效"依赖消费幂等性,实现思路(唯一键、Redis 标记等)参见消息幂等性与接口幂等性设计。

最大努力通知方案

这个方案的大致意思是:

  1. 系统 A 本地事务执行完之后,发送个消息到 MQ;
  2. 这里会有个专门消费 MQ 的最大努力通知服务,这个服务会消费 MQ,然后写入数据库中记录下来(或者放入内存队列也可以),接着调用系统 B 的接口;
  3. 要是系统 B 执行成功就 ok 了;要是系统 B 执行失败了,最大努力通知服务就定时尝试重新调用系统 B,反复 N 次,最后还是不行就放弃。

可以看出,最大努力通知是一种尽力而为、不做强保证的最终一致性方案:它没有消息表、没有 prepared 消息确认机制,只靠"消费消息 → 记录 → 定时重试 N 次"来尽量把结果通知到对端,适合对一致性要求低、允许最终失败由人工兜底的场景(例如某些通知类、对账类业务)。

你们公司是如何处理分布式事务的?——面试回答模板

如果你真的被问到,可以这么说:

  • 强一致性场景:我们某某特别严格的场景,用的是TCC来保证强一致性——比如严格资金要求绝对不能错的场景;
  • 最终一致性场景:其他一些场景基于阿里的RocketMQ实现分布式事务——比如订单插入之后要调用库存服务更新库存,库存数据没有资金那么敏感,就可以用可靠消息最终一致性方案

即选型原则可以概括为:资金类强一致用 TCC,一般业务最终一致用可靠消息(RocketMQ)

几点实战提醒

  • RocketMQ 版本注意:RocketMQ 3.2.6 之前的版本可以按照上述 prepared/confirm 思路使用事务消息;之后接口做了一些改变,使用时需以当前版本官方文档的接口为准;
  • 自研可行:如果你愿意,完全可以参考可靠消息最终一致性方案的思路,基于 RocketMQ 自己实现一套分布式事务——上述第 1~6 步就是完整的自研蓝图;
  • 兜底机制:无论选哪种方案,都要考虑重试、幂等与人工补偿(报警)这三个兜底手段,这正是仓库中幂等性与消息可靠性两篇文档反复强调的工程要点。

方案对比总览

方案一致性强度是否依赖数据库是否依赖 MQ实现成本典型场景
XA(两阶段提交)强一致强依赖(资源管理器)低(中间件/JTA 支撑)单块应用跨多库,不适合高并发
TCC强一致高(手写大量补偿代码)资金、支付、交易等短流程高一致性场景
Saga最终一致否(可异步编排)中(每步配一个补偿操作)长流程、多参与者(含外部/遗留系统)
本地消息表最终一致强依赖(业务库内消息表)消息可靠性要求高、并发量不大的场景
可靠消息最终一致性最终一致强依赖(如 RocketMQ)订单、库存等大多数分布式事务场景
最大努力通知最终一致(尽力而为)可选(记录用)对失败容忍度高、允许人工兜底的场景

结语

分布式事务没有银弹,关键在于按业务的一致性要求选型:资金强一致走 TCC,一般业务走可靠消息最终一致性(RocketMQ),长流程多参与者走 Saga,XA 与本地消息表则更多作为理解原理的参照。面试时能清晰讲出这 6 种方案的原理、优缺点与选型逻辑,并联系消息可靠性、幂等性等配套工程手段,就足以证明你对分布式事务有体系化的认知。进一步学习可继续阅读仓库中的分布式系统章节以及MQ 可靠性传输、消息幂等性等关联文档。

【免费下载链接】advanced-java😮 Core Interview Questions & Answers For Experienced Java(Backend) Developers | 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识项目地址: https://gitcode.com/doocs/advanced-java

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 5:46:18

YOLOv5自制数据集全流程指南:从图像采集标注到训练配置

如果你已经跑通了YOLOv5官方仓库的检测Demo,下一步大概率就是琢磨怎么训练自己的模型。而“自己的模型”这个词,翻译过来其实就是一句话:你得先有自己的数据集。我见过太多次这样的场景——有人照着教程把环境配好、权重下载好,然…

作者头像 李华
网站建设 2026/9/18 5:45:10

登录注册全链路拆解:从表单校验到JWT安全加固

登录注册这功能,乍一看无非就是两个表单配两个接口,但真要在生产环境里把它做扎实,中间藏的细节能写满好几页。我做全栈开发这几年,见过太多项目在账号体系上翻车,有些是上线第一天就被脚本刷注册,有些是用…

作者头像 李华
网站建设 2026/9/18 5:44:53

AI小说创作系统:动态技能组合与进化引擎实践

1. 项目概述:当小说创作遇上AI进化力去年帮朋友调试小说生成脚本时,我发现一个有趣现象:大多数AI写作工具在生成3-5个章节后就会陷入重复套路。这促使我尝试用Agent技术构建一个真正具备进化能力的创作系统——它不仅能写故事,还能…

作者头像 李华
网站建设 2026/9/18 5:41:21

Security-101 应用安全核心概念(AppSec Key Concepts)深度指南

Security-101 应用安全核心概念(AppSec Key Concepts)深度指南 【免费下载链接】Security-101 8 Lessons, Kick-start Your Cybersecurity Learning. 项目地址: https://gitcode.com/GitHub_Trending/se/Security-101 应用安全(AppSec…

作者头像 李华
网站建设 2026/9/18 5:39:53

React Native鸿蒙深度链接适配与优化实战

1. React Native鸿蒙深度链接适配实战:从原理到推送跳转优化作为一名在React Native跨平台开发领域深耕多年的开发者,我深刻理解在OpenHarmony平台上实现深度链接(Deep Linking)的痛点。不同于Android和iOS相对成熟的生态&#xf…

作者头像 李华