面试知识库
极高 困难

分布式事务方案#

一句话答案#

强一致用 2PC/XA(性能差),最终一致用 TCC / SAGA / 本地消息表 / 事务消息,根据业务场景选择:资金用 TCC,普通业务用事务消息,长链路用 SAGA。

核心要点

方案全景对比#

方案一致性性能代码侵入适用场景
2PC/XA低(同步阻塞)传统数据库跨库事务
TCC最终极高(3 个方法)资金核心(扣款/转账)
SAGA最终长链路跨多服务
本地消息表最终简单异步通知
事务消息最终低(推荐)普通业务解耦

各方案详解#

2PC / XA#

协调者 → 所有参与者: Prepare(预提交)
  ├── 全部 OK → 协调者发 Commit
  └── 任一失败 → 协调者发 Rollback
plaintext

代价:

  • 同步阻塞:Prepare 到 Commit 之间所有参与者持有锁
  • 协调者单点:协调者宕机 → 参与者一直持锁
  • 性能上限:通常 < 100 QPS(取决于锁等待时间)

适用: 遗留系统已有 XA 驱动、不想改代码、能接受性能损失。

TCC(Try-Confirm-Cancel)#

Try:     冻结资源(如冻结 100 元,非直接扣除)
Confirm: 确认执行(将冻结的 100 元真正扣除)
Cancel:  取消回滚(将冻结的 100 元释放回可用余额)
plaintext

核心要求:

  • 每个服务实现三个方法(Try / Confirm / Cancel)
  • Confirm 和 Cancel 必须幂等(可能被重复调用)
  • 需要事务协调框架(如 Seata TCC 模式)

代价: 代码侵入极高——每个服务的业务逻辑要拆成三步,开发量翻倍。

适用: 资金交易(扣款、转账)——值得为强一致付出高开发成本。

SAGA#

正向操作链:T1 → T2 → T3 → ... → Tn
如果 Ti 失败:执行补偿链 Ci-1 → ... → C2 → C1(逆序回滚)
plaintext

两种编排方式:

方式特点
编排式(Choreography)每个服务监听事件触发下一步,去中心化
协调式(Orchestration)中央协调器控制流程,集中管理

编排式适合简单链路(3-4 步),协调式适合复杂长链路。

代价: 补偿逻辑可能复杂(如”已发短信”如何补偿?→ 不能撤回,只能发补偿短信)。

适用: 长链路跨多服务(下单→扣库存→扣积分→发短信→记日志),每步都可能失败。

本地消息表#

1. 业务操作和写消息表在同一个本地事务中
   BEGIN;
     INSERT INTO orders ...;
     INSERT INTO message_table (event, status) VALUES ('order_created', 'pending');
   COMMIT;

2. 定时任务轮询 message_table → 发送到 MQ
3. 消费方处理完成 → 回调更新 status = 'done'
4. 发送失败 → 定时任务重试
plaintext

代价: 需要额外的消息表 + 定时轮询任务 + 消费方幂等。

适用: 没有支持事务消息的 MQ(如 Kafka 早期版本)、简单异步通知场景。

事务消息(RocketMQ)#

1. Producer 发送半消息(Half Message)→ MQ 暂存,Consumer 不可见
2. Producer 执行本地事务
   ├── 成功 → 发送 Commit → MQ 投递给 Consumer
   └── 失败 → 发送 Rollback → MQ 丢弃半消息
3. 如果 Producer 宕机没发 Commit/Rollback
   → MQ 定时回查 Producer 的本地事务状态
   → 根据查询结果 Commit 或 Rollback
plaintext

优势: 不需要额外的消息表和定时轮询,MQ 原生支持。

Kafka vs RocketMQ 事务消息:

维度RocketMQKafka
机制半消息 + 事务回查事务 Producer + 事务协调器
适用通用事务消息Exactly-Once 语义(流处理)
推荐业务事务场景首选流处理场景

选型决策矩阵#

问自己三个问题:
1. 是否需要强一致?
   ├── 是(资金/账户)→ TCC
   └── 否(最终一致可接受)→ 继续问
   
2. 链路有多长?
   ├── 2-3 个服务 → 事务消息(低侵入)
   └── 4+ 个服务 → SAGA(补偿链)
   
3. 有没有成熟的 MQ?
   ├── 有 RocketMQ → 事务消息
   ├── 只有 Kafka → 本地消息表 / Kafka 事务
   └── 没有 MQ → 本地消息表 + 定时任务
plaintext

用订单场景串联#

下单 → 扣库存 → 扣余额 → 发通知

环节方案原因
下单 + 扣库存事务消息库存最终一致即可,不需要强一致
扣余额TCC资金操作需要强一致,Try 冻结→Confirm 扣除
发通知本地消息表 / MQ通知是旁路操作,失败重试即可

所有环节都需要幂等 + 对账机制兜底。

面试回答(2分钟版)

分布式事务我按一致性要求选方案。强一致场景如资金交易,用 TCC——Try 冻结资源、Confirm 真正扣除、Cancel 释放,三个方法都幂等,代价是代码侵入高但值得。普通业务最终一致就够了,首选事务消息——RocketMQ 的半消息机制,Producer 先发半消息,本地事务成功再 Commit,失败 Rollback,Producer 宕机 MQ 会回查。长链路跨多服务用 SAGA,正向操作链 + 逆向补偿链。本地消息表是事务消息的替代,用于没有 RocketMQ 的场景。2PC 性能太差,通常不用于互联网场景。核心原则:大多数业务最终一致就够了,别为了强一致付出过高代价。所有方案都需要幂等 + 对账兜底。

追问与易错

追问方向:

  • “你项目用了哪种?”→ 结合项目讲,说清选型理由
  • “TCC 的 Cancel 失败了怎么办?”→ 重试 + 告警 + 人工介入,Cancel 必须幂等
  • “事务消息和本地消息表的区别?”→ 事务消息不需要额外表和轮询,MQ 原生支持回查
  • “为什么不用 2PC?”→ 同步阻塞、协调者单点、性能差,互联网场景几乎不用

易错点:

  • ❌ “分布式事务一定要强一致”——大部分业务最终一致就够
  • ❌ 不考虑补偿/对账——任何方案都可能有异常,对账是最后防线
  • ❌ “用了 SAGA 就不用幂等”——SAGA 的补偿操作也需要幂等
  • ❌ 混淆 TCC 和 2PC——TCC 是业务层面的,2PC 是资源层面的