☰
分布式事务面试100题:从2PC到TCC、Saga的完整解析
2026/10/5 2:52:41 网站建设 项目流程

面试的时候,分布式事务是后端绕不开的硬茬子。很多人聊到2PC能背出流程,但一问“协调者挂了怎么办”就卡壳;也有人能把TCC三个字母报出来,却分不清Try阶段该锁什么资源。这两年面试官也学精了,不再直接问概念,而是上来就扔一个“订单服务调用库存服务,怎么保证两边不超卖”的场景题。我利用DeepSeek把这些高频考点系统整理了一遍,又把答案一条条咀嚼、校准,沉淀成下面这100道题。不要试图死记硬背,先把每类题背后的“为什么”想透,面试才能应对追问。

这份题集按逻辑分为七个部分:从基础概念、一致性理论,到2PC/3PC、TCC/Saga,再到消息事务、框架落地和最后的压轴扩展题。前3类适合验证基础,中间3类是方案层重点,最后1类属于区分“背题的人”和“真做过项目的人”的题。你可以按目录挑薄弱环节刷。

1. 定义题不是白给分:事务的边界没搞清楚,后面全崩了

这类题经常放在一面开场。面试官不是真想听你背书,而是想快速判断你有没有把“本地事务”和“分布式事务”的本质边界想清楚。很多候选人一上来就背2PC流程,但问他“本地事务为什么在微服务下失效”就答不透了。下面这12道题,每一道都是后续所有方案的基础,值得逐字推敲。

题1:什么是本地事务?答:本地事务指在单一数据库实例内完成的事务。它由数据库自身保障原子性和持久性,典型实现是以事务日志和锁机制为基础,比如MySQL的InnoDB通过undo日志回滚、redo日志持久化。应用里用Begin Transaction和Commit/Rollback包裹一个业务操作。

题2:数据库事务的ACID是什么?答:A是原子性,事务里的操作要么全做要么全不做;C是一致性,事务执行前后数据都要满足业务规则;I是隔离性,并发事务之间不能随便看到未提交的数据;D是持久性,事务提交后数据不能丢。这四点是事务可靠性的基石。

题3:什么是分布式事务?答:分布式事务是跨越多个独立资源管理器(数据库、消息队列、微服务)的事务。它需要协调各参与者,使整个跨节点操作最终达到一致状态。与本地事务不同,分布式事务没有一个数据库单点来统一控制提交和回滚。

题4:本地事务和分布式事务的核心区别是什么?答:核心区别是资源边界和决策方式。本地事务只控1个数据库,由本库的日志和锁直接决定是否提交;分布式事务要协调多个节点的独立事务,需要引入额外的协调机制(如协议、消息、状态表),并且无法依赖单一的本地锁或undo日志完成回滚。

题5:哪些业务场景最容易引出分布式事务?答:典型的有三类:第一是订单创建并扣减库存;第二是账户转账跨多个子账户或跨用户;第三是下单后发积分、发优惠券。这些场景共同点是同一业务动作要修改多个独立存储,任何一个失败都会造成数据不一致。

题6:分布式事务里的“参与者”和“协调者”分别指什么?答:协调者是发起全局事务并决定最终提交或回滚的角色,比如Seata的Transaction Manager;参与者是被纳入事务并实际执行本地操作的节点,比如订单库、库存库各自执行的本地事务。两阶段提交中,协调者发指令,参与者执行并上报结果。

题7:为什么同步RPC会放大分布式事务难度?答:因为同步调用会长时间占用连接、线程和数据库锁。分布事务里如果一边执行成功,另一边调用超时,谁来回滚、怎么通知对方回滚,都变得复杂。长事务还会拉高死锁概率,导致数据库连接池被拖垮。

题8:Redis的事务算分布式事务吗?答:不算。Redis的MULTI/EXEC只保证单节点上多个命令不被打断,但不支持传统意义的事务回滚,也没有跨节点协调。它解决的是命令的批量原子执行,不是ACID意义上的事务,更不是跨库的分布式事务。

题9:数据库主从复制场景是不是分布式事务?答:不是,主从复制是数据冗余和可用性方案。主库写入后异步同步到从库,可能短时间读不到最新数据,这是复制延迟,不是事务一致性。如果主从同步过程中主库宕机,还可能丢数据,这和跨库事务要解决的是两码事。

题10:强一致性、最终一致性、弱一致性怎么区分?答:强一致性指数据更新后,任何后续读取立刻能看到最新值;最终一致性指数据更新后,经过一段时间所有副本会收敛到一致,但期间允许读到旧值;弱一致性介于两者之间,对读取延迟和时间窗口没有严格保证。分布式事务的不同方案对应不同一致性强度。

题11:分布式事务中的“一致性”到底指什么?答:指全局业务状态的一致性,即各个参与者在事务结束或最终收敛时,看到或保存的数据状态反映同一个业务结果。比如订单已支付,库存必须已扣减,账户积分必须已发放,不能出现订单成功但库存未扣。

题12:分布式事务处理的核心目标是什么?答:在跨节点、跨资源的情况下,要么让所有本地操作都成功提交并对外可见,要么都回滚且不留下脏数据。如果不能做到强一致,则要保证数据最终能收敛为一致状态,并尽量避免中间态对业务造成不可控影响。

2. 一致性理论与CAP:面试官真正想听的推导逻辑

CAP题不是单纯让背“选两个”,面试官更想听你能不能把“为什么不能兼顾”的逻辑说清楚,以及落到项目里怎么取舍。BASE和隔离级别在这个环节经常一起考,容易混淆。建议把这组题和后面的方案题关联起来:每个方案都是对某一对CAP属性的选择。

题13:CAP理论里的C、A、P分别是什么?答:C是强一致性,所有节点在同一时刻读到相同数据;A是可用性,每个请求都能在合理时间内得到响应,不保证数据最新;P是分区容错性,网络分区发生时系统仍能继续运行。分布式系统必须面对网络分区,所以P必须满足。

题14:为什么CAP三者最多只能同时满足两个?答:当网络分区发生时,节点间消息无法互通。如果你保证强一致性,某些节点就必须拒绝请求,于是损了可用性;如果你保证所有请求都有响应,不同节点可能返回不同数据,于是损了一致性。因此同一时刻只能取舍C和A。

题15:实际分布式事务里怎么取舍CAP?答:绝大多数互联网业务选择AP+最终一致性,因为不能因为某个节点故障就让整站不可用。强一致场景只用在资金、账本、库存超卖等有限地方,用“短时间锁或串行化”换取一致性。没有一概而论,按业务容忍度选。

题16:BASE理论是什么?为什么它比ACID更契合分布式?答:BASE是Basically Available(基本可用)、Soft State(软状态)、Eventually Consistent(最终一致)的缩写。它允许系统在分区期间保持可用,数据暂时不一致,但在最终会收敛。因为分布式环境下网络开销和故障无法避免,牺牲强一致能换取更好的可用性和性能。

题17:ACID和BASE最大的区别是什么?答:ACID强调的是事务提交那一刻的强一致,执行期间的状态外界看不到;BASE则接受执行期间存在中间状态、允许数据不同步,只要最终收敛即可。ACID靠库锁和日志,BASE靠业务补偿和异步对账。

题18:最终一致性怎么实现?答:常见实现包括:本地消息表+定时任务、MQ事务消息、事件回溯、以及定期对账补偿。核心思路是先把主业务落库,再可靠地发出事件或消息,消费方执行后续动作,然后借助重试、对账把可能出现的不一致修正。

题19:数据库隔离级别和分布式事务一致性有什么关系?答:数据库隔离级别解决的是本地事务并发之间能看到什么数据,分布式事务的一致性解决跨节点业务状态收敛结果。但两者会互相影响,比如Saga的本地操作如果隔离级别太低,中间态被外部读到,容易造成业务脏读和重复处理。

题20:READ COMMITTED和REPEATABLE READ有什么区别?答:READ COMMITTED只防止脏读,同一事务里两次查询可能结果不同;REPEATABLE READ保证事务内多次读相同行结果相同,但可能遇到幻读。MySQL InnoDB默认REPEATABLE READ,通过MVCC和间隙锁处理了部分幻读。分布式事务场景要关注锁范围和性能。

题21:读未提交有什么危险?答:读未提交允许读到事务未提交的数据,事务一旦回滚,读到数据就是脏数据,会导致后续库存、订单逻辑基于错误数据继续处理。生产环境几乎不会用这个级别,只有在个别想让数据“尽快可见”的分析场景偶尔使用。

题22:可串行化是什么?分布式事务里需要吗?答:可串行化是最高隔离级别,相当于所有事务串行执行,结果与某个顺序执行完全一致。分布式事务如果全部要求可串行化,性能会非常差。实际中一般只对核心资源做等价串行化,比如数据库行锁、Redis分布式锁或唯一约束。

题23:幻读和分布式事务有什么关系?答:幻读指事务内两次范围查询返回了不同行数,通常由其他事务插入数据导致。在分布式事务里,如果多个服务操作同一批数据,且没有统一隔离设计,会出现类似“先查库存可扣,再次确认时库存变了”的问题,本质上是并发隔离失效。

题24:为什么没有隔离级别的事务是有风险的?答:没有隔离级别,多个并发事务会直接读到未提交数据,造成脏读、不可重复读和幻读,账目会乱,库存会超卖。事务隔离级别是保障单库一致性的基础;如果分布式事务的本地子事务不给足隔离,即使全局协调成功,业务数据还是会出错。

题25:分布式事务里如何实现隔离性?TCC和Saga能保证吗?答:TCC通过资源预留(Try加锁)降低并发冲突;Saga通常不做全局锁,靠乐观控制和补偿。两者都不能像数据库2PL一样保证完整的全局隔离,所以必须在业务层加幂等、防悬挂和对账机制。分布式事务和隔离级别是设计中各自独立的维度。

题26:什么是全局事务ID?它有什么作用?答:全局事务ID是贯穿整个分布式事务的唯一编号,用来串联所有参与者的本地操作、日志和消息。作用有三个:关联各分支事务、去重和幂等判断、故障恢复时定位整条事务链路。

题27:什么是事务上下文传递?主要方式有哪些?答:事务上下文就是全局事务ID、分支事务ID、锁模式等信息,要在服务调用链路里传递。常见传递方式是RPC隐式参数透传、MQ消息头附加、ThreadLocal跨线程传递。只有上下文传到位,后续节点才能知道自己属于哪个全局事务。

题28:跨线程传递事务上下文有什么坑?答:ThreadLocal只在当前线程内共享,如果业务用异步线程池执行子任务,上下文会丢;如果线程复用,旧上下文还可能污染新任务。解决办法是用包装器显式传值并清理线程变量,或者用全链路Trace框架统一透传。

3. 两阶段提交的磁盘文件与三阶段的超时优化

2PC是最经典的分布式事务协议,但很多面试者以为它只是“两个阶段发消息”。其实面试官真正关心的是它在协调者宕机、网络超时、参与者失联时会不会卡死,以及3PC为什么没能彻底解决这些问题。这组题建议配合“故障脑图”来记。

题29:两阶段提交(2PC)的过程是什么?答:第一阶段是准备阶段,协调者向所有参与者发送prepare请求,参与者执行本地事务并写日志,然后返回yes/no;第二阶段是提交或回滚阶段,协调者根据所有返回值决定全局提交或回滚,并通知各参与者执行commit/rollback。

题30:2PC的两个阶段分别做了什么?答:准备阶段主要做资源锁定和预提交,事务已执行但未持久化提交,只记录undo/redo日志;提交阶段根据所有参与者是否可提交,决定一次性全员commit或全员rollback。核心目的是让所有节点决策一致。

题31:2PC的协调者需要具备什么能力?答:协调者需要具备事务状态持久化能力,记录每个参与者的准备结果;需要超时机制和故障恢复逻辑;还要有完整的网络交互能力。生产级协调者往往要维持在事务表中的状态机,以便宕机重启后按状态继续提交或回滚。

题32:2PC有哪些致命缺点?答:最大缺点是同步阻塞,事务中每个参与者都持有数据库资源锁,等待其他参与者响应;其次协调者单点故障,如果它宕机,参与者只能一直阻塞;再就是脑裂风险,协调者恢复后难以统一决策,可能导致部分参与者提交、部分回滚。

题33:什么是2PC的阻塞问题?给个真实例子。答:比如订单服务、库存服务、积分服务都收到了prepare指令,前两个返回yes,积分服务一直网络超时。订单库里已扣的库存会被锁住,后续其他订单无法使用同一库存行,直到协调者超时回滚才能释放。这时候用户付款下单也可能跟着阻塞。

题34:如果2PC协调者宕机了怎么办?答:协调者宕机后,参与者无法收到最终提交/回滚指令,只能等待。解决办法是协调者把事务状态写本地持久化存储,重启后读取状态恢复指令;也可用协调者集群或高可用方案。但仅靠2PC协议本身并没有优雅的解决办法。

题35:什么是3PC?它解决了2PC的哪些问题?答:3PC在2PC基础上增加了预处理阶段CanCommit和超时机制,同时参与者在超时后可以主动释放资源或按照既定策略提交。它减少了阻塞时间,解决了一部分协调者故障单点问题,但增加了协议复杂度,并没有从根上解决脑裂。

题36:3PC的CanCommit、PreCommit、DoCommit各做什么?答:CanCommit先询问参与者“能不能执行”,尽量提前发现不可执行的情况,避免浪费资源;PreCommit让参与者真正执行事务并写日志;DoCommit通知大家统一提交。如果参与者超时未收到DoCommit,它可能自行提交或回滚(视实现而定)。

题37:3PC为什么仍然不完美?答:3PC引入了超时,但如果网络分区导致部分节点收到提交消息、部分节点超时自行回滚,就会产生部分提交。也就是说3PC把阻塞变短了,但没有实现完美的一致性。实际生产中用纯3PC的例子不多,大家更倾向于用真实状态机或消息事务替代。

题38:2PC和3PC在实际生产中用得多吗?答:原生2PC/3PC用得少,因为它们太重、数据库锁持续时间长,在微服务规模下很难受。很多中间件只是借用了2PC的思想,比如Seata AT模式借鉴两阶段思想但不要求全程锁资源;XA协议也基于2PC模型,但通常用于单库多分支场景。

题39:数据库XA协议和2PC有什么关系?答:XA是数据库参与2PC的标准接口规范。事务管理器(TM)通过XA接口协调多个资源管理器(RM)做prepare和commit/rollback。Java里JTA事务基于XA。但它跨数据库网络通信时性能损失明显,且容易出现全局锁等待问题。

题40:XA事务模型是什么?Spring的JTA事务怎么理解?答:XA模型就是全局TM+多个RM的模型。Spring的JTA事务(JtaTransactionManager)把多个数据源纳入同一全局事务,底层调用数据库XA驱动。实际项目里要配置XA数据源、事务管理器,并且处理两阶段提交的prepare阶段。

题41:2PC中的“两阶段”和消息事务的“两阶段”有什么区别?答:2PC的两阶段是针对多个数据库资源的prepare和commit;消息事务的两阶段通常指“发送半消息”和“本地执行后确认提交消息”。消息事务不需要所有参与者同时锁住数据库资源,而是通过消息回查达到最终一致,阻塞性小得多。

题42:面试官问“你用过2PC吗”,应该怎么答?答:先承认原生2PC在生产中很少裸用,再说理解:2PC具备强一致模型但阻塞严重;然后结合经验说在订单与库存场景里,我用Seata AT或事务消息替代;如果真在低并发内部系统用过XA,可以说当前量的上限在哪里。这样能展示专业知识,不只会背书。

4. TCC和Saga不是二选一,是场景二选一

TCC和Saga是柔性事务里面最常考的两种模式。很多候选人只把TCC的Try/Confirm/Cancel背得滚瓜烂熟,但问他“Try阶段是不是必须加资源锁”就答不上来。Saga则总有人把“反向操作”和“回滚”混为一谈。这组题是方案设计的关键,也是面试官最愿意深挖的地方。

题43:TCC是什么?Try、Confirm、Cancel各做什么?答:TCC把事务拆成三个阶段:Try做资源预留或检查,业务状态临时冻结;Confirm真正执行业务提交并释放预留;Cancel回滚预留并恢复资源。比如库存服务在Try阶段预占库存,Confirm阶段扣减,Cancel阶段释放。

题44:TCC为什么要分三步,不能一步锁住资源吗?答:一步锁资源就是2PC思路,整个事务期间资源都不可用,导致阻塞。TCC先用Try做短时间预留,把可判定结果放到Confirm时再执行,这样锁持有的窗口变短,业务并发能力更高。代价是需要业务操作天然支持“预留/确认/取消”三个动作。

题45:TCC的Try阶段一般做什么?答:Try阶段做业务检查和资源预留。比如转账里检查余额并冻结转账金额;订单里检查库存并预占库存。Try阶段若失败就触发Cancel,各参与者回滚本地的预留。注意Try不能真正把业务数据改完成态,否则Cancel会很难做。

题46:Confirm阶段如何保证最终一致?答:只要Try阶段成功,Confirm就默认必须成功。Confirm阶段要幂等,重复调用不能产生额外扣减;补做真正的业务变更,把冻结状态变成完成状态。如果Confirm失败,一般依赖重试机制,而不是走Cancel,因为不能两边都变。

题47:Cancel阶段如果失败会怎么样?答:Cancel失败会导致已预留资源无法释放,业务数据卡在中间态。通常需要记录Cancel状态并提供重试,同时要人工告警排查;补充方案是定时对账清理。Cancel也要幂等,否则重复取消可能把已恢复的数据再扣一次。

题48:TCC如何解决空回滚、幂等、悬挂?答:空回滚指Try还没执行就做了Cancel,要识别事务状态,不执行反向操作;幂等是Confirm和Cancel重复调用要安全,常用唯一事务ID判断;悬挂指Try在Cancel以后才到达,需要拒绝或丢弃迟到Try请求。三种问题都要靠事务状态表在业务侧控制。

题49:什么叫TCC的空回滚?怎么避免?答:空回滚是指Try阶段因为网络超时或未执行,协调者直接发起了Cancel,而Cancel里没有可回滚的数据。避免方法是在Cancel收到后先查事务记录:如果Try未执行,就不做任何操作;如果Try已执行,再执行取消逻辑。这笔状态记录要持久化。

题50:TCC的悬挂问题为什么会出现?答:悬挂指Cancel先执行完,Try请求才慢悠悠到达,这时候Try又会重新预留资源,造成资源一直被占用却不被Cancel。解决方法是幂等控制和请求时效校验,收到Try时先检查是否已Cancel,如果已有Cancel记录就直接忽略Try。

题51:TCC里怎么设计幂等控制?答:常见做法是给每个事务生成唯一分支ID,在事务状态表里记录主键;Try/Confirm/Cancel执行前先查状态表,如果状态已存在就返回成功。也可以利用数据库唯一约束,确保同一分支事务的请求只处理一次。

题52:TCC的事务状态是否需要独立存储,为什么?答:需要,因为靠内存记录无法保证宕机后恢复。状态表里至少要记录全局事务ID、分支ID、当前阶段(Try/Confirm/Cancel)、更新时间。独立存储可以是独立的数据库表,也可以是Redis加持久化,重点是不能在故障时丢状态。

题53:TCC和2PC的本质区别是什么?答:2PC是资源层协议,事务期间占用数据库写锁等待全局决定;TCC是业务层拆分,通过三个业务动作减少锁的持有时间。TCC把冲突和补偿逻辑上移到业务,所以实现复杂,但可用性和并发能力显著优于2PC。

题54:Saga是什么?核心思想是什么?答:Saga是一种长事务解决方案,把一个全局事务拆成多个本地事务,每个本地事务都有对应的补偿动作。执行过程中如果某一步失败,就反向执行之前所有步骤的补偿。它不要求各阶段立刻对外隔离,经常配合异步消息编排。

题55:Saga的正向操作和反向操作怎么设计?答:正向操作就是正常的业务动作,比如订单创建、扣库存;反向操作是业务层的补偿动作,比如取消订单、回补库存。反向操作必须能察觉到正向操作是否完成,并且最好幂等。补偿里的数据往往是业务状态变更,而不是数据库行锁回滚。

题56:Saga的补偿顺序是串行还是并行?为什么?答:通常串行执行,按照正向执行的逆序逐个补偿。串行能避免依赖关系的困扰:比如先回补库存,再释放支付预授权,再取消订单。并行虽然快,但多步正向操作之间有数据依赖,并行补偿很容易出现资源还没释放就去补充新的数据的情况。

题57:什么场景适合TCC,什么场景适合Saga?答:TCC适合需要强一点隔离性、资源可冻结的业务,比如资金账户冻结、库存预占;Saga适合流程长、中间步骤多、最终一致允许的业务,比如订票、下单后多个服务协作。核心判断标准是业务允不允许中间状态被别人看到,以及有没有轻量的撤销方式。

题58:Saga没有全局状态管理,谁负责协调补偿?答:两种方式:一是编排式Saga,由一个中心协调器按顺序调用各参与者,失败时中心负责不断重试补偿;二是协同式Saga,每个服务监听上一个服务发出的事件,自己决定下一步和补偿动作。面试里建议说编排式更适合可控性。

5. 本地消息表/MQ事务消息:订单与库存不丢单的套路

工作中最常用、最容易实现落地的一致性方案其实是“消息+幂等”。面试官也很爱考这块,因为这类问题能看出你是否真正理解异步和最终一致。本地消息表和RocketMQ事务消息,本质都是在回答同一个问题:本地业务执行和发消息这两个动作,怎么保证不丢、不重、顺序可控。

题59:什么是本地消息表?核心思路是什么?答:本地消息表是在消息发送方所在的数据库里建一张消息表,业务数据和消息数据放在同一个本地事务里提交;然后通过后台任务扫描消息表发送消息,收到消费确认后更新消息状态。核心是利用本地事务保证“业务成功=>消息一定写入”。

题60:本地消息表为什么先写业务数据,再写消息表?答:两者必须在同一个数据库事务里提交,无所谓谁先谁后,但必须在事务内。如果业务数据成功、消息写入失败,事务回滚,业务不会生效;如果业务失败但消息发出去,消费端就会处理一个不存在的数据。同库同事务是本地消息表的关键。

题61:本地消息表为什么能保证最终一致?答:因为业务数据和消息表同库原子写入后,消息投递即使失败,后台扫描任务也会不断重试;消费端在处理消息时再配合幂等控制,最终上下游的数据状态会收敛一致。即使中间有延迟,也不会出现业务成功但消息丢失的情况。

题62:本地消息表频繁扫表有什么问题?怎么优化?答:频繁扫表会产生大量无效查询,尤其消息表数据涨上去后,索引也会拖慢业务库。常见优化是给状态字段加索引并批量捞取,把即将投递的消息先加载到内存队列,用定时任务分批处理;更彻底的方案是直接用RocketMQ事务消息,把扫描这个动作交给MQ组件完成。

题63:什么是MQ事务消息?答:事务消息是消息队列对“本地业务和发消息原子性”问题的封装。发送方先发送半消息,不上消费者可见;在本地事务执行完成后,再确认提交或回滚消息;如果本地事务状态不明确,MQ端会主动回查发送方,要求反馈事务结果。

题64:RocketMQ事务消息的半消息是什么?答:半消息是已被MQ接收但暂不可被消费者消费的状态消息。它主要用来探测发送方本地事务是否已准备完成。半消息有两种后续:发送方明确提交,消息转为可消费;发送方明确回滚,消息被删除,不会被消费者看到。

题65:RocketMQ事务消息的本地检查是什么?答:本地检查是指当MQ长时间没有得到事务二次确认时,MQ会通过回调机制回查发送方,询问该条本地事务到底提交了没有。发送方需要能根据业务事务状态表回答“已提交”或“未提交”,然后MQ据此提交还是回滚消息。

题66:RocketMQ事务消息回查解决了什么?答:回查解决了“本地事务提交成功但消息确认请求丢失”或“发送方在确认前宕机”的问题。通过回查,MQ能在故障恢复后重新确定事务结果,避免消息漏发或错发。这正是事务消息比普通消息可靠的核心原因。

题67:Kafka支持事务消息吗?和RocketMQ有什么差异?答:Kafka支持事务性写入和跨分区的原子提交,也支持类似事务消息的机制,但它的强项在吞吐,事务流程配置和回调模型比RocketMQ复杂。RocketMQ的事务消息更贴近“业务回查”模型,用起来更直观。面试里不必争论好坏,要说清各自适用场景。

题68:什么是“最大努力通知”?答:最大努力通知是指发送方发出业务处理结果后,通过多次重试把消息传递给接收方,并配合接收方主动查询和对账机制,尽可能保证对方收到通知。它只保证尽量通知,不保证严格的事务一致性,比如支付回调后通知商家系统。

题69:最大努力通知和可靠消息最终一致的区别是什么?答:最大努力通知没有强依赖“业务本地事务”和“消息写入”的原子性,它更强调发送方的尽力投递和接收方主动查询;可靠消息最终一致则通过半消息或本地消息表,严格保证业务成功必然产生可投递消息。后者更适合核心资金、库存链路。

题70:订单创建和库存扣减:先扣库存还是先锁订单?答:没有标准答案,但要避免超卖和空订单。很多团队选择先创建订单,再扣减库存,失败则取消订单;也有团队先预占库存后再创建订单。关键是必要时加一个库存预占状态,防止两个请求同时扣到同一件库存。面试时主动给出你选择的理由更重要。

题71:事务消息和本地消息表有什么区别?答:本地消息表要自己在业务库里建表、写定时任务扫描,逻辑完全可控但维护成本高;事务消息由MQ负责半消息和回查,写起来更省心,但依赖中间件能力。两者核心一致,都是“业务和消息同事务”,最终都要靠消费端幂等接收。

题72:消息重发会导致什么问题?如何应对?答:消息重发最典型的问题是重复扣库存、重复发券、重复转账。应对必须在消费端做幂等:写入唯一消息ID、用业务主键做唯一约束、状态机校验前置状态。生产环境里不能依赖“MQ不会重发”,一定要默认消息可能重复。

题73:消费端如何保证幂等?列出三种方案?答:第一,用数据库唯一约束,比如订单号或消息ID做唯一键;第二,用Redis分布式锁加状态位,先处理再置为已处理;第三,用业务状态机判断,例如只有“待支付”能变成“已支付”,重复消息到达时就跳过。这三种可以组合用。

题74:为什么消息队列不是分布式事务,而是解决分布式事务的手段?答:消息队列不提供事务ACID,它提供的是可靠异步通信和重复消费控制。用它解决分布式事务,需要配合事务消息、幂等表、状态机和对账等额外机制。MQ只是“可靠消息”的载体,事务一致性依然要靠业务设计。

题75:消息发送成功但消费失败怎么修复?答:先看消费失败原因:如果是数据校验问题,通常要人工补数据或用重试队列;如果是依赖的下游服务故障,则让消息进入重试队列,延迟消费;多次重试仍失败,要转人工或发告警。修复时要保证手动补偿操作也记录到状态表里,避免二次修复。

题76:消息堆积会影响事务一致性吗?答:会影响“时间窗口”而不是“最终一致性”。比如订单已支付,但库存扣减消息在堆积,短时间内用户会看到库存没减少,业务中间态延长。如果MQ堆积超过容灾时间,还可能触发消费者降级,所以堆积监控必须纳入分布式事务治理体系。

6. Seata/RocketMQ/最大努力通知:工程落地避坑题

前面那些题偏理论,到了这里面试官就会问“你到底用过什么组件”。Seata是目前国内最主流的分布式事务框架,RocketMQ则是最常见的消息事务载体。这部分题把你从“懂概念”拉到“有工程经验”的档位。如果没有真实生产经验,至少要把每个组件的定位和坑点说清楚。

题77:Seata是什么?答:Seata是开源分布式事务中间件,支持AT、TCC、Saga、XA四种事务模式。它提供全局事务协调、分支事务注册、事务状态记录和恢复能力,让业务代码通过注解即可接入,最常用的是AT模式和TCC模式。

题78:Seata的AT模式核心是什么?利用了什么机制?答:AT模式的核心是“两阶段+自动补偿”。一阶段业务SQL正常执行并生成undo日志;二阶段如果是提交,则清理undo日志,如果是回滚,则根据undo日志还原数据。它不需要业务写TCC的三段代码,因此接入成本低。

题79:Seata AT模式的一阶段、二阶段是怎么做的?答:一阶段,业务SQL在本地库执行,Seata拦截SQL并生成镜像(before image和after image),记录到undo_log表,并注册分支事务;二阶段,全局提交时删除undo_log,全局回滚时根据镜像反向生成补偿SQL,把数据恢复原样。整个过程依赖数据库和代理机制。

题80:Seata AT模式为什么能“无侵入”?答:因为它通过数据源代理和SQL解析器拦截应用发的SQL,自动生成回滚镜像。开发者不需要像TCC那样自己写Try/Cancel逻辑。代价是要求数据库支持事务隔离、表必须有主键,且SQL复杂度高时解析可能不够完善。

题81:Seata TCC模式与AT模式相比有什么优劣势?答:TCC模式需要业务方实现Try/Confirm/Cancel三个方法,开发量大但能精确控制资源锁和补偿逻辑;AT模式不用写补偿但依赖数据库undo机制,且对复杂SQL和跨表操作支持有限。并发场景下AT的锁冲突可能比TCC更严重。

题82:Seata Saga模式在Seata中如何使用?答:Seata Saga通常通过JSON状态机定义步骤和补偿动作,由状态机引擎按序执行。每一步可以是Dubbo/RPC调用、HTTP调用或本地方法,失败后根据定义反向执行补偿。它适合长链路、无法提前冻结资源的业务。

题83:Seata与ShardingSphere、MyBatis-Plus整合要注意什么?答:注意数据源代理顺序,Seata要拿到业务数据源做增强;MyBatis-Plus的SQL生成要和Seata的SQL解析器兼容;ShardingSphere分库分表会改变SQL路由,undo_log表必须在每个库都有;还要关注多数据源下的事务边界定义。

题84:微服务中如何让Feign调用和Seata事务绑定同一全局事务?答:通过Seata的上下文传播机制,全局事务发起方把XID放入RPC请求头,Feign拦截器或Seata自带适配器从请求头取出XID,带到下一个服务。下一个服务的本地事务会被注册为同一全局事务的分支。要确保拦截器顺序不会被业务拦截器覆盖。

题85:事务分组和配置中心对Seata的重要性?答:Seata服务端通过事务分组识别不同业务集群的注册和路由;配置中心(Nacos/ZooKeeper)负责push事务配置、服务发现和启动参数。不配置事务分组或配置中心选错,会导致客户端连不上TC,这时报错很隐蔽,会表现为“全局事务未注册”。

题86:RocketMQ在分布式事务中扮演什么角色?答:RocketMQ主要承担可靠事件消息通道,典型是事务消息(半消息+回查)和普通可靠消息。它负责把业务事件可靠地送达消费者,并配合发送端事务状态表实现最终一致性。它本身不解决多个数据库资源的强一致,真正的一致靠消费端的幂等和业务校验。

题87:电商下单链路:下单、减库存、清购物车,你会选哪种方案?答:先说结论,我一般选“本地消息表或RocketMQ事务消息,配合消费幂等”。理由:购物车可以延迟清,库存建议在支付成功后扣减而不是下单时扣,这样能降低超卖和无效占用;核心是订单状态更新与消息发送同事务,库存消费失败就重试,同时定期对账。

题88:银行转账场景为什么用TCC而不是最终一致性?答:转账涉及资金,中间状态不能被外部可见,也不能出现“转出已扣、转入未到”的长期不一致。TCC通过冻结和确认能做到较短时间隔离,比异步消息更接近强一致;而且资金账户支持冻结、解冻、扣减,天然适合Try/Confirm/Cancel模型。

题89:怎么区分“柔性事务”和“刚性事务”?答:刚性事务通常指基于数据库ACID的分布式强一致方案,比如2PC/XA,所有操作要么一起成功要么一起失败,适合低并发、强一致;柔性事务允许暂时不一致,通过重试、补偿、状态机逐步收敛,如TCC、Saga、最终消息,适合高并发、可容忍短暂中间态。

题90:框架选型时要重点对比哪些指标?答:看几点:一是事务模式支持(AT/TCC/Saga);二是侵入性,是需要改业务代码还是代理数据源;三是性能损耗,尤其锁持有时间和额外SQL;四是运维复杂度,是否需要额外部署组件;五是扩展和生态,能否和公司现有RPC、MQ、配置中心打通。

题91:有没有不需要中间件的分布式事务方案?答:有,比如数据库原生XA(但要数据源支持)、本地消息表自研定时任务、基于事件驱动的Saga、以及业务数据幂等+重试表。不用中间件的好处是运维简单,坏处是很多底层逻辑要自己写,而且调试时容易漏状态,不适合复杂链路。

题92:分布式事务压测主要关注哪些指标?答:主要看吞吐量、事务成功率、接口延迟、资源锁等待时间、中间件CPU/内存和数据库连接池占用。还要观察到“有故障比无故障时多耗多久”,例如模拟一个库存服务超时,看整个链路是否能及时回滚,会不会拖垮全局线程池。

7. 压轴烧脑题:幂等、隔离、死锁与恢复

面试官把综合题放最后,一般是想看你有没有真实碰见过生产事故。这里没有标准答案,更重要的是你的排查思路和取舍逻辑。下面这些题不仅是考点,也是你在分布式事务方案设计里绕不开的隐患点。

题93:全局唯一ID和分布式事务有什么关系?答:全局唯一ID用来关联整个事务链路,同时也是幂等键、状态表主键和消息去重依据。没有全局唯一ID,事务恢复时没法判断哪一步已经执行、哪一步还没执行;异常重试也会造成重复操作。所以很多分布式事务框架的第一个数据项就是全局事务ID。

题94:分布式事务中怎么设计重试机制?答:重试必须生成独立任务记录,记录重试次数、下一次执行时间;重试时执行幂等逻辑;要给重试设置最大次数和退避策略,超过上限进人工或对账列表。同时注意不能用同步调用线程直接重试,否则会拖挂核心接口线程池。

题95:为什么说幂等是分布式事务的基石?答:因为分布式环境下网络超时、消息重发、协调者重复决策都是常态。如果不做幂等,一次Cancel被重复执行就会多扣一次款,一次Confirm被重复执行就会多发一次积分。幂等保证“同一操作执行一次和执行多次结果一致”,是各种一致性方案的底层前提。

题96:分布式锁和分布式事务是什么关系?会不会冲突?答:分布式锁通常解决并发抢资源问题,分布式事务解决跨节点一致性问题。如果先加锁再开事务,锁可能覆盖多个分支,导致长事务持锁;如果锁和事务不在一层,又可能出现事务还没提交锁已释放的情况。要明确锁的粒度,最好锁内只做短操作,把耗时的业务计算放到事务外。

题97:如何排查分布式事务卡住、转账超时?答:第一看协调者事务状态表,找到卡在准备态或提交态的事务;第二看各参与者本地undo_log和事务表,确认哪个分支没有返回完成;第三看网络和数据库连接池,是否有大量锁等待;最后根据事务ID查log链路,定位到具体服务和SQL。大多数“卡住”都源于数据库行锁等待和协调者失联。

题98:分布式事务里死锁一般怎么处理?答:从设计上尽量避免死锁,比如所有服务按统一顺序访问资源(都是库存后积分);设置数据库锁等待超时参数,让超时事务先回滚;在TCC/AT模式下,全局事务重复竞争同一行数据很容易触发死锁,通常靠让一个事务快速失败并重试来化解。发现死锁后要保留现场日志,不能简单重启。

题99:分布式事务是否可以用最终一致性替代?什么情况下不能替代?答:大部分业务场景可以,只要接受短暂中间态并做好对账。但有些场景不能:涉及资金清算,要求实时的强一致且中间状态不能泄露;业务规则要求“同一秒内不能出现两边状态不一致”的合规场景,以及并发量极低的内部系统,直接XA/2PC反而更保险。

题100:你如何向面试官总结分布式事务的方案选型?答:我会说,先看业务的一致性容忍度:账务类强一致优先选TCC或XA;跨服务订单中长链路用Saga;可靠异步事件用本地消息表或RocketMQ事务消息;如果团队能够接受AT的自动化补偿,Seata AT是最低侵入的选择。最后一定要强调幂等、状态表和对账是整个方案的底座,缺了它们任何方案都会在故障时露馅。


整理完这100道题,我自己有个明显感受:分布式事务其实没有“银弹”,每种方案都是在一致性、可用性、性能和实现复杂度之间做取舍。面试时与其死背每个协议的步骤,不如把“什么场景选什么方案、为什么”想透。如果你能用上面这些思路把一个订单链路讲清楚,再抛出一个你踩过的坑,面试效果会比背完100道题好得多。希望这份清单能成为你准备面试的工具箱,而不是又一个收藏夹里的压箱底列表。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询