分布式事务面试的 12 个追问:CAP 与 BASE、2PC/TCC/Saga/事务消息四方案对比,以及补偿失败、幂等设计、消息不丢、顺序保证与对账验证等分水岭问题。
“分布式事务怎么保证一致性?”——这道题的难点不在于说出几种方案,而在于能扛住面试官一连串的追问:你的方案在部分失败时怎么办?补偿失败了怎么办?怎么保证幂等?
本文按真实追问链组织,12 个问题层层递进,覆盖从理论到落地的完整路径。
一、先问清楚:为什么要分布式事务
Q1:为什么单体事务(本地事务)不能直接用于微服务?
本地事务依赖单个数据库连接的 ACID。微服务下数据分散在不同库、不同服务,一次业务操作可能要改多个库,本地事务管不到别人的库。
Q2:CAP 到底在说什么?
在网络分区(P)不可避免的前提下,只能在一致性(C)与可用性(A)之间取舍。注意常见误区:CAP 不是”三选二”的简单开关,而是在分区发生时的取舍,正常状态下系统可以同时保证 CA。
Q3:BASE 理论怎么理解?
基本可用(Basically Available)、软状态(Soft state)、最终一致性(Eventually consistent)。它的工程含义是:接受短暂不一致,用补偿与对账换取可用性——这是绝大多数互联网业务的实际选择。
二、方案对比:四选一

Q4:2PC(两阶段提交)有什么问题?
协调者故障会导致参与者一直锁定资源(阻塞);第二阶段协调者宕机可能出现部分提交的数据不一致。它的优点是强一致、实现简单(如 XA 事务),缺点是性能差、可用性低。
Q5:TCC 是什么?和 2PC 的区别?
TCC 是业务层面的两阶段:Try(预留资源)→ Confirm(确认)或 Cancel(释放)。区别在于:2PC 在数据库层锁资源,TCC 在业务层做预留,锁粒度更细、性能更好,但需要为每个操作写三套代码,侵入性极强。
Q6:Saga 模式怎么工作?
把长事务拆成一系列本地事务,每个步骤都有对应的补偿操作。任一失败时,反向执行已完成步骤的补偿。优点是不长期占锁、适合长流程;缺点是缺乏隔离性,可能出现”脏读”(看到中间状态)。
Q7:本地消息表 / 消息队列事务消息怎么做?
核心是把”业务操作”与”消息发送”放在同一个本地事务里:先写业务数据 + 消息表,再由投递任务把消息发出去,消费方保证幂等。这是落地成本最低、使用最广的最终一致方案。
-- 同一个本地事务里写业务数据与消息记录
BEGIN;
UPDATE account SET balance = balance - 100 WHERE id = 1;
INSERT INTO local_message(id, topic, payload, state)
VALUES (uuid(), "topic-transfer", '{"from":1,"to":2,"amt":100}', "INIT");
COMMIT;
-- 异步投递任务:只投递 INIT 状态且达到重试间隔的消息
SELECT id, payload FROM local_message
WHERE state = "INIT" AND next_retry <= now()
LIMIT 100 FOR UPDATE SKIP LOCKED; -- 多实例防重复投递
-- 投递成功置 SENT,失败累加 retry_count 并按退避推后 next_retry
三、追问区:这里才是分水岭
Q8:补偿失败了怎么办?
三层防线:① 补偿操作必须可重试(幂等);② 重试要有最大次数与退避策略;③ 超过阈值进入人工干预队列并告警。任何分布式事务方案都必须设计”最终兜底”,否则就是定时炸弹。
Q9:怎么保证幂等?
常见四种手段:
- 唯一键/唯一索引:数据库层兜底,最可靠;
- 去重表:用业务唯一 ID 记录已处理的请求;
- 状态机:只允许特定状态迁移(如”待支付 → 已支付”),重复请求因状态不符被拒绝;
- Token 机制:前置申请一次性令牌,服务端校验后删除。
Q10:消息会丢失吗?怎么保证不丢?
三个环节都要防:
生产端——事务消息或本地消息表 + 确认机制;
broker 端——持久化 + 多副本;
消费端——先执行业务再提交位点(注意这样会产生重复消费,所以幂等是必须的)。
Q11:怎么保证消息顺序?
只在需要保证顺序的业务键上路由到同一分区(如同一订单 ID 的消息进同一分区),同时消费端串行处理该分区。全局有序代价极高,业务上几乎不需要。
Q12:最终一致怎么验证一致性没被破坏?
对账是最后一道防线:定时比对上下游数据(如订单与账单),发现差异自动修复或告警。同时建立一致性监控指标(如悬停事务数量、补偿失败率),纳入告警。
加分答法:主动说出”我们优先用事务消息 + 幂等 + 对账这套组合,只有在资金核心链路才上 TCC”。这说明你在权衡,而不是堆砌名词。
四、一个完整的答题框架
被问到”如何设计 X 场景的事务”时,按这个顺序答:
- 先确认一致性要求:是否必须强一致?能否接受秒级/分钟级延迟?这一步决定方案区间;
- 给出方案及理由:说明为什么选它、放弃了什么;
- 说明失败处理:重试、幂等、补偿、兜底;
- 补充验证机制:对账与监控指标;
- 提一句降级:极端情况下能否走人工流程。
五、小结
分布式事务没有银弹,只有权衡。面试中真正拉开差距的,不是知道多少种协议,而是能否说清”为什么在这个场景选这个方案,以及它失败时会怎样“。
记住一句话:能用最终一致解决的,别上强一致;必须强一致的,把范围缩到最小。
延伸阅读: microservices.io · Saga 模式。方案实现细节请以所使用的消息队列与框架官方文档为准。
#分布式#后端面试题#事务#面试

