想象你是一位婚礼策划师,要在 10 月 1 日办一场盛大婚宴。后厨要准时备菜,花艺要按时布置,场地方要提前确认档期——每个环节都依赖一个独立的供应商,任何一家掉链子,整场婚宴都可能砸锅。更棘手的是,时间没法撤销:如果婚礼前三天场地突然没了,后厨订的食材、花艺买的鲜花,都得想办法处理。 在软件世界里,一次下单要扣库存、减优惠券、创建订单,这些操作散落在不同的服务与数据库里。让它们像婚宴一样“要么全成、要么全撤”,就是分布式事务要解决的事。本篇用“办婚宴”这条故事线,讲透四种主流协作协议——其中两种像“群发请柬统一开席”和“提前预演再开席”,另两种像“逐道上菜、失败撤菜”和“先锁档期后付款”。读完你不仅懂理论,还能在架构选型时做出靠谱判断。

岗位关联

对 AI 从业者的价值:大模型应用、推荐系统、交易系统背后都离不开数据一致性设计;掌握分布式事务的取舍逻辑,是架构师与资深工程师面试和方案评审中的硬通货。

目录
  1. 可视化演示
  2. 事务基础概念
  3. 两阶段提交
  4. 三阶段提交
  5. Saga 模型
  6. TCC 模式
  7. 选型决策框架
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

动态展示2PC两阶段提交、3PC三阶段提交、Saga补偿事务与TCC确认取消四种方案对比。

本节配有交互动效(进入视口后自动加载,约 3KB)。

事务基础概念

假设你一个人在家做一桌菜:买菜、洗菜、炒菜、上桌,全由你掌控,任何环节出错你都能立刻补救——这是“单机事务”的舒适区。但在婚宴里,你只是总指挥,后厨、花艺、场地方各有各的账本。把“一个人的事”拆给多方协作,问题立刻浮现:他们没有共享的厨房,无法像你一样随时回退。

数据库中的“事务”指一组要么全部成功、要么全部失败的操作,靠四个性质保证:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),合称 ACID。单体数据库里,一个事务可以覆盖多张表;但分布式系统里,数据分散在不同服务、不同数据库中,一次业务操作要跨越多个“账本”,本地事务就有心无力了。

分布式系统还要面对三个现实约束——网络分区、机器宕机、时钟偏差。这些约束催生了 CAP 定理:一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance),最多只能同时满足两个;而网络分区无法避免,所以工程上真正要做的,是在“强一致”与“可用性”之间二选一。弱化强一致、接受“最终一致”,是构建高可用系统的常见妥协。

就像婚宴策划要先定调“追求仪式感还是灵活性”,分布式事务的起点也是先想清楚一个问题:你到底能不能接受短暂的不一致?这个答案,将决定后面所有方案的选择。

C+A+P≤2\displaystyle C + A + P \le 2
CAP 定理:一致性、可用性、分区容错三个指标,最多同时满足两个;因为分区无法避免,实际选择只剩“一致”与“可用”二选一。
  1. 字母定义C 代表一致性,A 代表可用性,P 代表分区容错性。三个字母各取 0 或 1,表示“这项指标是否被满足”。
    C+A+PC + A + P
  2. 最多取二不等号 ≤ 2 表示三个指标最多同时满足两个,这是分布式系统绕不开的根本约束。
    ≤2\le 2
  3. P 不可放弃网络分区由物理环境决定,无法避免,所以 P 恒为 1,真正的选择只剩 C 与 A 的对决。
    P=1⇒C+A≤1P = 1 \Rightarrow C + A \le 1

两阶段提交

最“仪式感”的做法:总指挥群发请柬——“10 月 1 日有档期吗?”后厨回“能”,花艺回“能”,场地方回“能”,好,锁定档期,统一通知“开席”;只要有任何一个说“不能”,全部取消预订。这就是二阶段提交(2PC):把“协调多方”简化成“问一遍、定一次”。

2PC 引入一个协调者(总指挥)和若干参与者(供应商)。第一阶段“投票阶段”:协调者向所有参与者发送准备请求,参与者锁定相关资源、写好回滚日志后,回复“就绪”或“中止”。第二阶段“提交阶段”:协调者收集投票,若全票就绪则广播提交指令;只要有一票中止,则广播中止指令。

2PC 能提供强一致性,代价却是两个著名缺陷:一是同步阻塞——参与者在投票后一直持有资源锁,直到收到最终指令,期间其他事务被挡在门外;二是协调者单点故障——如果协调者在第二阶段宕机,参与者既等不到提交也等不到中止,只能无限期阻塞,甚至出现部分已提交、部分未提交的不一致。

婚宴上,大家为了等总指挥一声令下,从上午十点干等到下午三点:厨师不敢开火、花艺不敢撤棚。如果总指挥中途晕倒,这顿婚宴就永远卡在“备而不开”的僵局里——这就是 2PC 最真实的写照。

Commit  ⟺  V1∧V2∧⋯∧Vn\displaystyle \text{Commit} \iff V_1 \wedge V_2 \wedge \cdots \wedge V_n
2PC 的提交判定:只有所有参与者都投“就绪”票,协调者才会广播提交;只要有一票“中止”,全局回滚。
  1. 投票变量 $V_i$$V_i$ 表示第 $i$ 个参与者投票阶段返回的结果,取值为“就绪”或“中止”。
    Vi∈{就绪,中止}V_i \in \{\text{就绪}, \text{中止}\}
  2. 逻辑与 $\wedge$把所有参与者的投票用“且”连接起来,任何一个为假,整体就为假。
    V1∧V2∧⋯∧VnV_1 \wedge V_2 \wedge \cdots \wedge V_n
  3. 提交条件$\iff$ 表示“当且仅当”,全票就绪才广播提交,否则广播中止。
    Commit  ⟺  V1∧V2∧⋯∧Vn\text{Commit} \iff V_1 \wedge V_2 \wedge \cdots \wedge V_n

三阶段提交

为了不让大家干等,总指挥把流程改成三步:先问“能开席吗?”;得到肯定后发“预开席”,让厨师先把菜备好;最后才发“正式开席”。更重要的是,每位供应商都戴了手表——如果总指挥失联太久,他们不再傻等,而是按约定自行决定去留。这就是三阶段提交(3PC)。

3PC 在 2PC 的两阶段之间插入一个预提交阶段,形成 CanCommit → PreCommit → DoCommit 三段式。CanCommit 只做可行性探测,不锁资源;PreCommit 让参与者真正准备资源并回复确认;DoCommit 才正式提交。每一阶段协调者都可能因超时中止,参与者同样有超时机制:在预提交后如果收不到正式提交指令,会基于“多数派已准备”的事实自行提交或回滚。

3PC 用超时机制解决了 2PC 中协调者故障导致的无限阻塞,也缩小了阻塞窗口;但它并非完美:为了多一轮消息交互,正常路径的延迟比 2PC 更高;而且在极端网络分区下仍可能“脑裂”——部分参与者超时自行提交,另一部分超时自行回滚,数据照样不一致。因此工程上 3PC 用得并不多,更多是借鉴它“超时 + 多阶段确认”的思想。

给每个人都戴上手表,确实避免了“干等总指挥”;但如果大家等到的信号不一致——有人听到“开席”上了菜,有人听到“取消”撤了桌——那这场婚宴的混乱,可能比等待等出来的更糟。

ttimeout=tpre+Δt\displaystyle t_{\text{timeout}} = t_{\text{pre}} + \Delta t
3PC 的超时公式:参与者从进入预提交时刻 $t_{\text{pre}}$ 开始计时,等待窗口 $\Delta t$ 内没收到正式指令,就自行决策。
  1. 基准时刻 $t_{\text{pre}}$参与者进入预提交状态(菜备好、等指令)的时间点。
    tpret_{\text{pre}}
  2. 等待窗口 $\Delta t$协议约定的最大容忍等待时长,超过这个窗口就触发超时处理。
    Δt\Delta t
  3. 超时自决$t_{\text{timeout}}$ 一到还没收到正式指令,参与者按预案自行提交或回滚,不再无限等待。
    ttimeout=tpre+Δtt_{\text{timeout}} = t_{\text{pre}} + \Delta t

Saga 模型

换一种思路:婚宴不搞“统一开席”,而是十道菜一道一道上。每上一道,就当作“已成事实”;如果第四道主菜烧糊了,就按相反顺序把第三道、第二道、第一道撤下去,让宾客当作没吃过这道菜。这就是 Saga:用“撤菜”代替“锁桌”。

Saga 把一个大型分布式事务拆成一组有顺序的本地事务 $T_1, T_2, \dots, T_n$,每个 $T_i$ 都在自己的服务内独立提交;同时为每一步提前准备一个“补偿事务” $C_i$。一旦第 $k$ 步失败,就逆序执行 $C_{k-1}, \dots, C_1$,把已生效的操作一一撤销。

Saga 不持有长事务锁,并发性能和可用性远高于 2PC;代价是一致性弱化为“最终一致”——中间状态对外可见,业务必须容忍。两种协调方式:编排式(Choreography),每个服务完成后发事件驱动下一个服务,去中心化;协同式(Orchestration),由中央编排器集中调用各服务并负责补偿编排。电商下单、多仓拆单等长链路场景常用 Saga。注意,补偿事务不是简单地“删数据”,而是要设计成业务级的逆向操作——比如把库存加回“可用额度”,而不是删掉流水记录。

“撤菜”听起来轻松,其实有成本:后厨的食材钱、花艺的损耗退不回来,还得向宾客道歉。所以 Saga 的补偿事务必须提前设计好,甚至要有人工对账任务兜底,才能保证最终一致。

S=T1∘T2∘⋯∘Tn,Tk 失败时执行 Ck−1∘⋯∘C1\displaystyle S = T_1 \circ T_2 \circ \cdots \circ T_n, \qquad T_k\ \text{失败时执行}\ C_{k-1} \circ \cdots \circ C_1
Saga 的核心:正向事务按顺序执行,一旦某步失败,就逆序执行之前各步的补偿事务,让系统回到初始状态。
  1. 正向序列整个 Saga 是若干本地事务按顺序串联,$\circ$ 表示“前一个完成后,下一个才开始”。
    T1∘T2∘⋯∘TnT_1 \circ T_2 \circ \cdots \circ T_n
  2. 定位失败步 $T_k$第 $k$ 步失败后,$T_k$ 及其之后的步骤都不能再执行。
    Tk→失败T_k \to \text{失败}
  3. 逆序补偿从 $C_{k-1}$ 开始依次执行到 $C_1$,每步补偿都与对应的正向事务配套,后做的先撤销。
    Ck−1∘Ck−2∘⋯∘C1C_{k-1} \circ C_{k-2} \circ \cdots \circ C_1

TCC 模式

再回到婚宴筹备的“订食材”环节。精明的总指挥不会立刻全款下单,而是先“锁档期”:告诉供应商“10 月 1 日的食材我预留了,先别卖给别人,但我还不付款”。等后厨、花艺、场地方全都锁定成功,才统一“确认下单”付款;任何一个环节锁失败,就通知所有人“取消预留”。这就是 TCC。

TCC(Try-Confirm-Cancel)把业务操作拆成三个接口:Try——做资源检查与预留,不真正改动业务数据;Confirm——真正执行业务,让预留的资源生效;Cancel——释放资源,回到 Try 之前的状态。协调者先调用所有参与者的 Try 接口,全部成功再逐个调 Confirm;任一失败则逐个调 Cancel。

相比 2PC,TCC 不用全局锁,而是把“锁”放进了业务语义——比如冻结金额、预占库存——性能和灵活性都好得多,非常适合金融转账、库存扣减等场景。但它把一致性保证转嫁给了业务方:三个接口都要实现幂等;还要处理“空回滚”(Cancel 到达时 Try 还没成功,要安全返回而非报错)和“悬挂”(Try 迟到于 Cancel,要拒绝执行)等边界问题,实现成本不低。

锁定档期、确认成交、取消释放——这套玩法像极了现代婚宴的预订制:钱没付、位置不丢,任何一个供应商没档期就全部取消。业务规则越清晰,TCC 就越顺手;前提是每个供应商都愿意配合你的“锁档”逻辑。

Try⇒{Confirm,所有 Try 成功Cancel,任一 Try 失败\displaystyle \text{Try} \Rightarrow \begin{cases} \text{Confirm}, & \text{所有 Try 成功} \\ \text{Cancel}, & \text{任一 Try 失败} \end{cases}
TCC 的分支逻辑:所有 Try 成功则进入确认阶段让业务生效;任何一个 Try 失败则进入取消阶段全部释放。
  1. Try 先行所有参与者先调用 Try 接口完成资源检查与锁定,为后续决策收集凭据。
    Tryi,  i=1,2,…,n\text{Try}_i,\; i = 1, 2, \dots, n
  2. 全成功 → Confirm当且仅当所有 Try 都成功时,协调者才对每个参与者调用 Confirm 接口,让业务真正生效。
    ∀i:  Tryi=成功⇒Confirmi\forall i:\; \text{Try}_i = \text{成功} \Rightarrow \text{Confirm}_i
  3. 任一失败 → Cancel只要有一个 Try 失败,协调者对全部参与者调用 Cancel 接口,释放已锁定的资源。
    ∃i:  Tryi=失败⇒Canceli\exists i:\; \text{Try}_i = \text{失败} \Rightarrow \text{Cancel}_i

选型决策框架

办婚宴没有唯一正确答案:传统家庭要排场,宁可让宾客等也要全场一致上菜(对应 2PC/3PC);新派家庭讲究体验,菜一道道上,哪道翻车也能圆场(对应 Saga);宴会厅预订讲究档期锁定,钱和位置都不含糊(对应 TCC)。选型,本质是看清“你办的是什么样的宴”。

四个方案在一致性、延迟、可用性、实现成本之间各有取舍:2PC/3PC 提供强一致,但阻塞风险高;Saga 提供最终一致、可用性高,但没有隔离性,中间态可见;TCC 提供业务层面的最终一致、性能好,但要求业务方实现三个接口并处理幂等与空回滚。选型框架可以浓缩为三个问题:业务能容忍短暂不一致吗?事务链路长不长、并发高不高?团队能承担多少实现成本?

具体到生产场景:金融转账、账务系统通常选 TCC(冻结/解冻资金)或 2PC(低并发的内部链路);电商下单、库存与支付多服务协作通常选 Saga(协同式编排)来保证最终一致和可用性;中小团队想快速落地,优先考虑 Saga 编排加消息对账。另外,任何分布式方案都无法 100% 消除不一致窗口,必须配套事务消息或定时对账任务兜底。

把四种方案放在婚宴的时间轴上:2PC 是“全场齐开席”的仪式派,3PC 是“预演防呆”的稳妥派,Saga 是“随上随撤”的灵活派,TCC 是“先锁后付”的商务派。看清自家宴席的需求再选方案,你就能在风暴中稳稳掌勺。

Score=w1×一致性−w2×延迟−w3×恢复成本\displaystyle \text{Score} = w_1 \times \text{一致性} - w_2 \times \text{延迟} - w_3 \times \text{恢复成本}
选型评分公式:把一致性作为收益,把延迟和恢复成本作为代价,用权重表达业务偏好,算出每个方案的综合分。
  1. 权重 $w$三个权重由业务特征决定——资金类业务把 $w_1$ 调大,高并发业务把 $w_2$ 调大。
    w1,  w2,  w3w_1,\; w_2,\; w_3
  2. 正向项一致性一致性收益是加分项,一致性越强,对业务越有利。
    w1×一致性w_1 \times \text{一致性}
  3. 负向项成本延迟与恢复成本是扣分项,用减法把它们计入总分,提醒你“强一致不是免费的”。
    −w2×延迟−w3×恢复成本- w_2 \times \text{延迟} - w_3 \times \text{恢复成本}

AI 应用场景

电商大促下单链路

阶段:业务链路设计

解决的问题:传统下单流程里,库存扣减、优惠券核销、订单创建各自独立,任何一个服务失败都会导致“库存扣了但订单没建成”或“券用了但单子丢了”。AI 与商业系统双修的工程师用 Saga 协同式编排把整条链路串起来:每步成功才走下一步,任一步失败就自动触发逆序补偿(库存回滚、券退回),大幅减少人工对账。

具体案例:一次大促秒杀中,下单服务创建订单成功后调用库存服务扣减,库存不足返回失败;Saga 编排器立即执行补偿:取消刚创建的订单、释放已锁定的优惠券,用户看到“库存不足”提示,系统数据保持一致。

金融账务冻结与划转

阶段:数据一致性保障

解决的问题:跨行转账要求“要么双方都到账,要么都不动”,传统接口重试可能造成重复扣款。采用 TCC 模式:Try 阶段同时冻结汇款方与收款方资金,Confirm 阶段完成真实划转,任一银行拒绝则 Cancel 解冻,把强一致的代价从“全局锁”转移为“业务冻结语义”。

具体案例:用户从 A 行卡向 B 行卡转账 5000 元,TCC 协调器先冻结 A 行余额并预登记 B 行入账,两边 Try 都成功后 Confirm 放行;若 B 行超时未确认,协调器对 A 行发起 Cancel 解冻,用户资金分毫未损。

多仓物流拆分与智能补货

阶段:预测结果落地与对账

解决的问题:AI 库存预测模型输出补货建议后,需要把订单拆到多个仓库发货,各仓独立提交子单;若某仓缺货,其余仓也需回退。用 Saga 配合消息对账,避免强一致锁拖垮高并发的库存服务,同时保证预测数据与库存数据的最终一致。

具体案例:某大仓预测系统建议补货 1000 件并将其拆为三仓各 300/400/300,第二仓库存不足,Saga 逆序撤销第一仓的出库预留,并触发采购补单消息给供应链系统。

Python 演示

python61 行
# 婚宴筹备仿真: 对比 2PC 与 Saga 的失败恢复import random  class Vendor:    """一位供应商: 后厨 / 花艺 / 场地方"""     def __init__(self, name, fail_rate):        self.name = name        self.fail_rate = fail_rate     def prepare(self, job):        """尝试完成一项准备工作, 按概率失败"""        if random.random() < self.fail_rate:            print("  [失败] " + self.name + " 无法完成: " + job)            return False        print("  [完成] " + self.name + ": " + job)        return True     def undo(self, job):        print("  [补偿] " + self.name + " 撤销: " + job)  def run_2pc(vendors, jobs):    """2PC: 先投票探测, 全部同意才统一执行"""    print("== 2PC 开始 ==")    for v in vendors:        if not v.prepare("档期探测"):            print("== 2PC 结果: 有人反对, 全局回滚 ==")            return    print("== 2PC 结果: 全票通过, 统一提交 ==")    for v, job in zip(vendors, jobs):        v.prepare(job)  def run_saga(steps):    """Saga: 逐道执行, 失败则逆序补偿"""    print("== Saga 开始 ==")    done = []    for step in steps:        name, do, undo_job, undo = step        if not do():            print("== " + name + " 失败, 开始逆序补偿 ==")            for _, _, uj, u in reversed(done):                u(uj)            return        done.append(step)    print("== Saga 结果: 全部成功 ==")  if __name__ == "__main__":    random.seed(42)    chef = Vendor("后厨", 0.05)    florist = Vendor("花艺", 0.30)    run_2pc([chef, florist], ["准备凉菜", "布置桌花"])    print()    steps = [        ("凉菜", lambda: chef.prepare("准备凉菜"), "凉菜", chef.undo),        ("桌花", lambda: florist.prepare("布置桌花"), "桌花", florist.undo),    ]    run_saga(steps)
目的:用“婚宴筹备”仿真对比 2PC 与 Saga 的失败恢复路径,演示全局回滚与逆序补偿两种思路。
  1. class Vendor:用“供应商”对象模拟婚宴中的后厨、花艺等服务方,各自持有失败概率。
  2. if random.random() < self.fail_rate:用随机数模拟服务方“今天掉链子”的概率,让失败可复现。
  3. def run_2pc(vendors, jobs):2PC 协调流程:第一阶段先探测每家能否就绪。
  4. if not v.prepare("档期探测"):投票阶段只要有一家返回失败,立即全局回滚。
  5. for v, job in zip(vendors, jobs):全票通过后进入第二阶段,统一执行真实业务。
  6. def run_saga(steps):Saga 编排器:把每一步与它的补偿动作打包成元组。
  7. name, do, undo_job, undo = step解包四元组:步骤名、执行函数、补偿名、补偿函数。
  8. done.append(step)记下已成功的步骤,供后续补偿使用。
  9. for _, _, uj, u in reversed(done):失败时逆序遍历历史步骤,逐个执行补偿事务。
  10. random.seed(42)固定随机种子,让演示结果可复现,便于讲解。

常见误区

误区“2PC 有投票、有提交,肯定能保证所有节点要么全成功、要么全失败,不会出任何问题。”

正解2PC 只在协调者正常存活时保证全局一致;协调者一旦宕机,参与者会陷入阻塞,甚至出现部分提交、部分未提交的“半成品”状态。就像总指挥晕倒后,厨师端着菜不知该不该上桌。生产环境必须为协调者做高可用,并配套故障恢复与人工对账。

为何常见:教材通常只画正常路径的时序图,学生看不到协调者宕机这一失败分支,容易把理想当现实。

误区“3PC 加了超时,肯定把 2PC 的所有问题都解决了,应该全面采用 3PC。”

正解3PC 用超时消除了“无限阻塞”,但网络分区下仍可能脑裂——不同参与者可能分别提交或回滚,数据照样不一致;而且多一轮交互让正常路径更慢,工程上 3PC 使用很少,更多是思想借鉴。戴手表解决了干等,但手表各自走字,还是会有人看错时间。

为何常见:看到“三阶段 > 二阶段”的简单比较,就误以为功能性包含,忽略了分布式系统中不存在完美协议。

误区“补偿事务就是把刚才执行的 SQL 反向执行一遍,比如库存扣了再加回去,很简单。”

正解补偿是业务级的逆向操作,要考虑幂等、并发与历史数据保留:库存要回滚到“可用额度”而非简单加回;已发出的短信、邮件可能无法撤回;补偿执行本身也可能失败,所以要设计重试与人工兜底。撤菜不是把菜从宾客嘴里挖出来,而是“再上一道能抵消影响的菜”。

为何常见:把补偿想成数据库 Undo Log 是惯性思维——本地事务的回滚由引擎完成,分布式补偿却必须由业务自己编写。

误区“Try 阶段把业务做了,Confirm 阶段走个过场,这样性能最好。”

正解Try 只做资源检查与预留(如冻结金额),业务数据在 Confirm 阶段才真正变化;让 Try 直接写业务数据,一旦后续 Cancel 就难以干净地撤销,违背 TCC 的设计初衷。锁档期和付款是两回事,提前付款的“预订”就不再是预订了。

为何常见:名字“Try“听起来像”尝试执行”,加上不少框架把 Confirm/Cancel 当可选项,容易产生误解。

误区“2PC 一致性最强,那所有分布式场景都用 2PC 不就行了?”

正解强一致通常以可用性和性能为代价,高并发电商与长链路业务根本扛不住长锁和阻塞。正确姿势是按业务容忍度选型:核心资金用 TCC/2PC,链路长、并发高的用 Saga 加对账。仪式感拉满的婚宴,可能让宾客饿肚子。

为何常见:把“更强的一致性”等同于“更可靠”,忽略了可用性和延迟本身也是系统可靠性的组成部分。

面试题

Q1. 二阶段提交协议(2PC)的两个阶段分别是什么?

单选题 · easy

  • A.查询与确认
  • B.投票与提交← 正确答案
  • C.尝试与确认
  • D.预演与执行

答案:B。2PC 的第一阶段是投票(准备)阶段,参与者锁定资源并表态;第二阶段是提交阶段,协调者根据投票结果广播提交或中止。B 正确。A、C 更像是 TCC 的术语,D 是干扰项。

Q2. 在 2PC 的投票阶段,参与者需要向协调者返回什么?

单选题 · easy

  • A.准备完成
  • B.确认回执
  • C.同意或反对← 正确答案
  • D.提交结果

答案:C。投票阶段参与者返回的是“就绪/同意”或“中止/反对”二选一的表态,协调者据此决定全局提交还是回滚,对应 C。A、D 是提交阶段才看到的结果,B 过于笼统。

Q3. 2PC 最常被诟病的缺陷是什么?

单选题 · easy

  • A.网络延迟过高
  • B.同步阻塞风险← 正确答案
  • C.数据冗余过多
  • D.隔离级别太低

答案:B。2PC 的参与者投票后要持有资源锁等待最终指令,期间其他事务无法访问,这就是同步阻塞;协调者宕机时阻塞可能变成无限期,故选 B。延迟高是 3PC 的问题,C、D 与 2PC 无关。

Q4. 与 2PC 相比,3PC 最重要的改进是什么?

单选题 · mid

  • A.引入超时机制← 正确答案
  • B.减少网络请求
  • C.增加事务隔离
  • D.消除数据丢失

答案:A。3PC 让参与者也拥有超时自决能力,协调者失联时不再无限阻塞,这是它相对 2PC 的核心改进。它同样可能出现脑裂与数据不一致,故 D 错误;3PC 多了一轮消息,B 说反了。

Q5. 3PC 协议三个阶段正确的先后顺序是?

单选题 · mid

  • A.询问、预提交、正式提交← 正确答案
  • B.准备、提交、回滚
  • C.尝试、确认、取消
  • D.开始、提交、结束

答案:A。3PC 的顺序是 CanCommit(询问)→ PreCommit(预提交)→ DoCommit(正式提交),对应 A。B 是 2PC 的变体描述,C 是 TCC 的三个接口,D 是常见事务伪流程。

Q6. Saga 中某个正向事务失败后,正确的处理方式包括哪些?

多选题 · mid

  • A.按相反顺序执行补偿← 正确答案
  • B.对失败环节有限重试← 正确答案
  • C.立即丢弃所有已提交
  • D.直接忽略该环节错误

答案:AB。Saga 失败后的标准动作是逆序执行之前步骤的补偿事务;在工程实践中,通常还会先对失败环节做有限次重试,重试仍失败才走补偿,故选 A、B。C、D 会让系统陷入不一致,明显错误。

Q7. Saga 的两种常见协调方式称为?

单选题 · mid

  • A.编排与协同← 正确答案
  • B.集中与分散
  • C.同步与异步
  • D.轮询与通知

答案:A。Saga 的两种协调方式是 Choreography(编排式,各服务发事件互相驱动)和 Orchestration(协同式,中央编排器统一调用),对应 A。B、C、D 是其他维度的概念,不用于描述 Saga 的协调模式。

Q8. TCC 中 Try 阶段的核心职责是什么?

单选题 · hard

  • A.完成全部业务逻辑
  • B.锁定资源预留资格← 正确答案
  • C.提交全部业务结果
  • D.释放已经锁定的资源

答案:B。Try 只做资源检查与预留,比如冻结金额、预占库存,不真正修改最终业务数据,故选 B。A、C 是 Confirm 阶段的事,D 是 Cancel 阶段的事。

Q9. 关于 TCC 的说法,正确的有哪些?

多选题 · hard

  • A.Try 类似两阶段提交的投票← 正确答案
  • B.TCC 由事务管理器强制回滚
  • C.Confirm 阶段不允许再失败← 正确答案
  • D.业务方需实现三个接口← 正确答案

答案:ACD。TCC 里 Try 的“锁定资源并反馈结果”与 2PC 的投票阶段高度相似,A 正确;TCC 没有全局事务管理器强制回滚,而是靠业务方实现 Confirm/Cancel 接口,B 错误;官方语义要求 Confirm 在 Try 成功后必须成功(幂等设计),C 正确;TCC 要求业务方实现 Try/Confirm/Cancel 三个接口,D 正确。

Q10. 协调者在 2PC 提交阶段宕机,可能产生哪些后果?

多选题 · hard

  • A.参与者会长期阻塞等待← 正确答案
  • B.可能出现数据不一致← 正确答案
  • C.事务一定会自动回滚
  • D.所有参与者会自动提交

答案:AB。协调者宕机后,已收到提交指令的参与者会提交,未收到的一直等待,导致 A 的长期阻塞和 B 的部分提交/部分未提交的不一致。事务不会自动回滚,参与者也不会自动提交,C、D 错误。