除夕夜,一家人要一起做一桌年夜饭。张爷爷是“总指挥”,负责统筹凉菜、热菜、汤品、甜点——每道菜都有一个负责人。如果大家各炒各的,有人五分钟出锅、有人半小时才起锅,最后桌上的菜有的凉透、有的才下锅,这顿饭就“不一致”了。于是张爷爷立下一条规矩:先备菜,再统一开火。所有人都把菜洗好、切好、腌好,举手向张爷爷报告“准备好了”;张爷爷确认十个人全部举手,才喊一声“开火”,大家一起下锅。这就是“先确认、后执行”的智慧。 在计算机的分布式系统里,“年夜饭”就是一次跨多个数据库或服务的业务操作——比如用户在电商平台下单,要同时扣库存、减余额、记订单。这些操作分散在不同机器上,怎么保证“要么全部成功、要么全部失败”?答案正是两阶段提交(Two-Phase Commit,简称 2PC)。它就是那位统筹全局的“张爷爷”:先让各节点“备好菜”(准备阶段),再统一“开火”(提交阶段)。 本文将从年夜饭的故事出发,带零基础的你完整理解 2PC:它为什么会出现、核心原理是什么、有哪些致命弱点,以及业界如何改进它。
掌握两阶段提交,是进入分布式系统世界的必修课。对后端工程师与架构师而言,理解 2PC 的原理与缺陷,意味着能在面试中展现对分布式事务本质的把握,也能在工程实践中避开最危险的“数据不一致”陷阱。
目录
可视化演示
演示分布式事务中两阶段提交的协调流程,从准备阶段Vote收集、提交阶段Commit广播到最终一致,展示参与者状态与阻塞、单点等关键问题。
本节配有交互动效(进入视口后自动加载,约 3KB)。
2PC 一致性协议 准备阶段:Vote收集 提交阶段:Commit广播 全票通过才提交 Vote×N 决定提交或回滚
分布式事务背景:一桌菜为什么不能一人做
年夜饭从来不是一个人的战斗。就算家里有位厨神,十道菜、两个灶眼、一场晚会的时间,也容不得他慢悠悠一道一道来。于是分工是必然的:有人蒸鱼、有人炖肉、有人拌凉菜。可分工之后新的麻烦出现了——怎么保证所有菜能在同一时刻热腾腾地上桌?如果各做各的,你炒完了我在切,你端上桌我在等油热,整桌菜就“不一致”了。
计算机世界也一样。早年的单体应用,所有数据都放在同一个数据库里,一个事务靠数据库自带的 ACID 特性就能保证“要么全成功、要么全失败”。可当业务越做越大,订单数据、库存数据、用户数据被拆到不同的数据库甚至不同的服务中,一个操作就可能要同时修改多个库。比如用户下单,订单库要插入一条记录,库存库要扣减一个数量——如果这两件事不在同一个数据库里,普通的本地事务就无能为力了。
于是,分布式事务登上了舞台。它要解决的核心问题是:让分散在不同节点上的多项操作,表现得像在同一个数据库里执行一样。这背后绕不开 CAP 理论——在分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition Tolerance)三者难以兼得,必须有所取舍。2PC 站在“强一致性”这一边,追求的是让所有节点始终保持一致。
这就好比十道菜每道有 1% 的失误率,全部顺利出锅的概率只有九成左右。靠“各凭运气”显然不行,必须请出一位总指挥来统一调度——两阶段提交由此登场。
- 单点成功率p_i 代表第 i 个节点单独顺利完成操作的概率,取值范围在 0 到 1 之间。
- 全部连乘∏ 是连乘符号,把 n 个节点的成功率逐一相乘,得到所有节点同时成功的概率。
- 概率衰减每个 p_i 都小于 1,乘的次数越多结果越小,这就是为什么节点越多越容易整体失败。
两阶段提交原理:先备菜,再统一开火
张爷爷把年夜饭的规矩定成了铁律。第一步,所有人都把菜洗好、切好、腌好,然后向张爷爷举手喊“我准备好了”;第二步,张爷爷看到十只手全部举起,才喊“开火”,大家一起下锅翻炒。若有人喊“我还没好”,张爷爷就宣布“今天不做了”,所有人放下刀铲,各回各家。这套流程听起来朴素,却是 2PC 最核心的思想。
在 2PC 中,张爷爷就是“协调者”(Coordinator),各位厨子就是“参与者”(Participant)。整个协议分两轮。第一轮叫“准备阶段”(Voting Phase),协调者向所有参与者发送 PREPARE 请求,参与者在本地执行事务操作、写入 undo/redo 日志,但并不真正提交,然后回复 YES 或 NO。第二轮叫“提交阶段”(Commit Phase),协调者收集全部投票——如果所有人都说 YES,就广播 COMMIT,所有参与者正式提交;只要有一个说 NO,就广播 ABORT,所有参与者回滚。
这里最精妙也最反直觉的一点是:参与者在“准备阶段”虽然干活了,但没有真正落锤。就像厨子们把鱼切好、肉腌好,但还没有下锅——此刻反悔完全来得及,倒掉食材重来即可。这种“先做预备工作、不急于定局”的设计,保证了最终决策可以全局统一。
张爷爷的决策规则也是如此:十个人都说“准备好了”就开火,有一个说“没Ready”就休整重来。所有菜同时下锅、同时上桌,年夜饭的“原子性”就有了着落。
- 投票变量v_i 表示第 i 个参与者的投票结果,只能取两个值——YES 或 NO。
- 合取运算⋀ 是逻辑“与”符号,要求括号内每个条件都成立;成立返回真,任何一个不成立就返回假。
- 等价判定⇔ 表示“当且仅当”,协调者只需检查这条规则,就能决定最终是 COMMIT 还是 ABORT。
关键协议流程:记好菜谱,遇到意外不乱套
张爷爷的规矩里还藏着一个细节:他要求每位厨子一边备菜,一边在菜谱本上记下自己切了什么、腌了什么、用了哪口锅。万一有谁中途肚子疼离场(故障),其他人可以翻开他的本子,替他把半成品收好或倒掉,绝不让“做到一半的菜”留在厨房里。
这正是 2PC 里的“日志记录”。协议不是简单发两条消息了事,每一步都伴随着持久化的日志,这是系统故障后能够恢复的依据。完整的流程可以拆成四步:首先,协调者向所有参与者发送“准备提交”请求,这是投票的发起;其次,各参与者执行本地事务,若成功就写入 undo/redo 日志并回复 YES,若失败则回复 NO;再次,协调者收集全部投票,依据上一节的合取规则做出全局决定,并把决定写进自己的日志;最后,协调者向所有参与者广播 COMMIT 或 ABORT,各参与者按指令提交或回滚。
日志是整个协议的“保险丝”。参与者如果不幸在准备阶段之后宕机,重启后可以根据 undo 日志回滚未完成的事务;协调者若在发出 COMMIT 后宕机,重启后也能靠日志找到“我当初答应了谁”,把 COMMIT 消息重新发给尚未收到指令的参与者。没有日志,故障恢复就无从谈起。
就像张爷爷数举起的手——十只手全部举起才喊开火,少一只都要喊停。菜谱本保证了无论谁临时离场,这顿饭的进度都不会丢。
- 示性函数$\mathbf{1}[\text{条件}]$ 是示性函数,条件为真时值为 1,为假时值为 0。
- 票数汇总∑ 将每个参与者的“1 或 0”累加,得到赞成票总数。
- 决策比较协调者将 N_yes 与参与者总数 n 比较,相等则 COMMIT,否则 ABORT。
两阶段提交的缺陷:堵车、指挥失灵与决策混乱
这套年夜饭方案听着完美,实际操作却有几个扎心的隐患。第一个:张爷爷必须等最慢的人。有位厨子备菜要三个小时,其他九个人就只能握着菜刀干等,谁也不敢先下锅。第二个:万一张爷爷自己心梗住院了,没人喊“开火”,十个人举着手面面相觑,这顿饭就无限期搁置。第三个:家里电话线路故障,张爷爷听不清谁举了手,有人以为开火了、有人以为还要等,厨房瞬间乱作一团。
这三个隐患恰好对应 2PC 的三大著名缺陷:同步阻塞、单点故障、脑裂。
同步阻塞是最直观的痛点。在准备阶段,参与者要持有资源锁(比如数据库的行锁),一直等到协调者给出最终指令。如果某个节点特别慢或网络抖动,其他节点只能陪着“堵车”,整个系统的吞吐量被最慢的节点死死卡住。这就是木桶效应——桶能装多少水,取决于最短的那块木板。
单点故障同样致命。协调者是整个协议的决策中心,它一旦宕机,所有参与者都悬在半空。经典的 2PC 没有超时机制,参与者不能自作主张提交或回滚,只能无限等待。就算协调者在几天后重启,恢复过程也相当复杂,因为各方状态可能已经各异。
更隐蔽的是脑裂。当协调者和部分参与者之间的网络被切断,系统被分成两个“派系”,协调者无法确认另一边的状态。它可能向能联系到的参与者发送了 COMMIT,但另一边的参与者根本没收到指令,最终出现一半提交、一半回滚的灾难性不一致。
一桌年夜饭能不能准时开席,取决于最磨蹭的那位厨子;而这个体系能不能正常运转,又全押在张爷爷一个人的健康上。两处都是单点,处处都是风险。
- 单个节点耗时t_i 表示第 i 个参与者完成准备并返回投票所花的时间。
- 取最大值max 运算符从 n 个 t_i 中挑出最大的那个,它是所有参与者共同等待的时间上界。
- 阻塞的代价这个最大值直接决定了事务从发起到落定需要多久,体现了“短板决定整体”。
改进方案对比:三阶段、消息队列与最终一致
为了挽救这套“总指挥”方案,各路大厨纷纷献计。有人提出把两阶段改成三阶段:在“备菜”和“开火”之间加一个“预开小火”的动作。张爷爷先宣布“我打算开火啦”,大家各自把锅热上,但先不正式下菜。这样一来,万一张爷爷突然失联,大家至少知道他已经起了意、热了锅,不至于手足无措。这就是三阶段提交(3PC)。
3PC 把原来的两轮扩展成三轮:CanCommit(询问能否提交)、PreCommit(预提交,通知做好准备)、DoCommit(正式提交)。关键改进是引入了超时机制——参与者在 PreCommit 之后若长期等不到协调者的指令,会默认执行 ABORT,而不是无限期阻塞。这个改动显著缩小了阻塞窗口,也降低了协调者单点故障的影响面。
另一派人则干脆放弃“同时出锅”的执念,提出“逐个上菜、凉了用保温罩”。这就是最终一致性方案,代表技术是本地消息表和事务消息。本地消息表把“发消息”和“业务操作”放在同一个数据库事务里,先写库、后发消息,发不出去就定时重试;事务消息则借助消息中间件(如 RocketMQ)先发送“半消息”,业务成功后确认投递,失败则回滚销毁。这些方案允许中间状态不一致,但通过重试和补偿保证所有节点最终能收敛到一致。
一顿饭想稳妥,就要多打几个电话、多确认几次,代价是大家多一点等待;想让菜凉得慢,可以用保温罩(消息队列),不必强求同秒出锅。从强一致到最终一致,本质是“对实时性的妥协”与“对可用性的追求”之间的一场长期谈判。
- 2PC 消息量n 个参与者各收到两条指令——PREPARE 和 COMMIT/ABORT,共 2n 条。
- 3PC 消息量3PC 有三轮通信——CanCommit、PreCommit、DoCommit,共 3n 条。
- 代价与收益3PC 多出的 n 条消息换来了参与者的超时自主权,降低了长期阻塞的可能。
工程实践应用:TCC、Saga 与选型智慧
真正的大饭店办宴席,绝不只用一套方案。有的菜讲究“同时上桌”,比如脆皮烤鸭,晚一分钟皮就不脆了;有的菜则宽松得多,比如凉菜拼盘,提前备好、依次端上即可。于是大厨的工具箱里备了多种模式:TCC、Saga、消息队列,各有用武之地。
TCC 模式把一个操作拆成三个阶段:Try、Confirm、Cancel。Try 阶段像“试菜”——先尝一口看口味是否合适,同时把座位预留好(冻结资源);口味合适就 Confirm(正式上菜),不合适就 Cancel(撤下这道菜)。它非常适合“需要预留资源”的业务,比如电商下单时先冻结库存、支付时冻结余额。TCC 的优点是灵活、不阻塞,缺点是需要业务方自己实现三个方法,改造量大。
Saga 模式则把长事务拆成一串本地事务,按顺序执行,每个步骤都配一个反向补偿操作。好比宴席上一道道上菜:先上凉菜、再上热菜、最后上甜点。如果热菜出了问题,就撤掉热菜、凉菜也一并收回(逆序补偿)。Saga 适合步骤多、周期长的业务,比如旅游预订——订机票、订酒店、订门票,任何一步失败就逐步取消前面的订单。
选型没有银弹,只有权衡。强一致要求极高、参与者少、事务短的场景,用 2PC;需要预留资源、并发不极端的场景,用 TCC;长事务、多步骤、允许中间态的场景,用 Saga;能容忍异步延迟的场景,用事务消息加本地消息表。一个成熟的系统,往往像大饭店的菜单一样,同时混用多种方案。
年夜饭也是这样:既要有统筹“同时出锅”的硬菜,也要有一道道依次上桌的凉菜。选对模式,饭局才能又稳又高效。
- 初始状态S_0 是事务开始前各节点都保持一致的状态基线。
- 增量累计Δ_k 表示第 k 步操作对系统状态的改变,正向操作取正值、补偿操作取负值。
- 收敛目标S_final 是所有节点最终达到一致的目标状态,中间允许短暂不齐。
AI 应用场景
智能电商平台的订单事务保障
阶段:AI 推荐引擎完成用户偏好预测、生成商品推荐之后,进入订单履约的业务落地阶段。
解决的问题:用户下单需要同时扣减库存、冻结账户余额、生成订单记录。传统单体事务无法跨越订单库、库存库、账户库三个数据库,一旦中途失败就会出现“钱扣了货没减”的严重事故。采用 TCC 模式,在 Try 阶段冻结库存与余额,Confirm 阶段完成真实扣减,Cancel 阶段统一释放,保证下单流程的强一致。
具体案例:某头部电商平台大促期间,AI 推荐命中率提升带来订单量暴涨。订单服务调用库存服务与账户服务时,利用 TCC 框架(如 Seata)实现了分布式事务:先预占库存,再冻结金额,最后确认或取消,将下单成功率从 92% 提升到 99.9% 以上。
跨行转账的资金安全守护
阶段:AI 反欺诈模型对转账行为进行实时风险评分,通过风控审核后的资金划拨阶段。
解决的问题:跨行转账涉及两家银行的账户系统,必须保证“扣款”和“入账”两个操作同时成功或同时失败。经典 2PC 在这里仍有价值——参与者少(通常两个)、事务短、对强一致要求极高,2PC 的全票通过机制恰好满足银行的合规要求。
具体案例:某股份制银行的核心系统实现跨行转账时,协调者向转出行和转入行同时发送 PREPARE,两家银行各自完成账户校验和资金预冻结并返回 YES,协调者再广播 COMMIT 完成划拨。整个过程通常控制在一秒以内,任何一方异常都触发全局回滚,杜绝了“转出成功但转入失败”的资金丢失。
微服务画像同步的最终一致
阶段:AI 实时更新用户画像(如兴趣标签、消费能力分群)后,需要将画像数据广播到会员、搜索、推荐等多个下游服务。
解决的问题:如果画像更新时同步调用所有下游服务,任何一个服务响应慢都会阻塞主链路。通过 RocketMQ 事务消息,将“更新画像库”和“投递画像变更消息”绑定在同一个本地事务中,消息先以半消息形式发送,业务提交成功后再确认投递,下游服务异步消费,实现最终一致。
具体案例:某内容平台每天有数亿次画像更新,采用事务消息后,画像变更以异步方式在数百毫秒内同步到推荐系统和搜索系统。即使某个下游服务短暂宕机,消息队列也会积压并在恢复后补发,用户感知不到任何数据延迟。
Python 演示
import random class Participant: def __init__(self, name, fail_prob=0.1): self.name = name self.fail_prob = fail_prob self.prepared = False self.committed = False def prepare(self): if random.random() < self.fail_prob: return "NO" self.prepared = True return "YES" def commit(self): self.committed = True def rollback(self): self.prepared = False self.committed = False class Coordinator: def __init__(self, participants): self.participants = participants def run_two_phase_commit(self): print("=== Phase 1: Prepare ===") votes = [] for p in self.participants: v = p.prepare() print(f" -> {p.name}: {v}") votes.append(v) if all(v == "YES" for v in votes): print("=== Phase 2: Commit ===") for p in self.participants: p.commit() print(f" -> {p.name}: committed") return "COMMIT" else: print("=== Phase 2: Abort ===") for p in self.participants: p.rollback() print(f" -> {p.name}: rolled back") return "ABORT" if __name__ == "__main__": random.seed(42) accounts = [ Participant("账户系统A", fail_prob=0.05), Participant("库存系统B", fail_prob=0.05), Participant("订单系统C", fail_prob=0.05), ] coordinator = Coordinator(accounts) result = coordinator.run_two_phase_commit() print(f"\n最终结果: {result}") for p in accounts: print(f" {p.name}: prepared={p.prepared}, committed={p.committed}")
if random.random() < self.fail_prob:用随机数模拟参与者准备阶段可能出现的故障,失败则返回 NO。self.prepared = True参与者执行本地事务并进入“已准备”状态,但此时并未真正提交。votes.append(v)协调者把每个参与者的投票收集到列表中,等待统一决策。if all(v == "YES" for v in votes):这是提交条件的代码化表达:所有投票必须全为 YES。for p in self.participants: p.commit()全票通过后,协调者广播 COMMIT,参与者逐一提交。p.rollback()只要有一票反对,协调者广播 ABORT,所有参与者回滚到事务前状态。
常见误区
误区总觉得用了 2PC 就万事大吉,任何故障下数据库都不会出现不一致。既然都“两阶段”了,先问再交,怎么还会错呢?
正解2PC 保证的是“无故障时的原子性”,而不是“任意故障下的绝对一致”。当协调者宕机、网络分区、COMMIT 消息丢失时,照样可能出现部分节点提交、部分节点未提交的不一致状态。它只是把不一致的概率大幅降低,并未完全消除。就像年夜饭的总指挥喊完“开火”后电话线断了,远处的厨师没听到指令,那锅菜就悬在半空。
为何常见:2PC 的“先问后提交”流程太过直观,容易让人产生“全员同意=必然成功”的错觉,忽略了消息丢失和节点宕机这些真实场景。
误区以为参与者收到 PREPARE 请求后就会立即提交事务,数据已经落库了。那如果后面收到了 ABORT,数据怎么回滚?
正解准备阶段只是“干了一半的活”——执行事务、写 undo/redo 日志、持有资源锁,但并没有真正提交。真正的提交必须等协调者的 COMMIT 指令。这也正是 2PC 的精髓:先做可撤销的预备工作,等全局统一决定后再落锤。好比厨子把菜切好腌好,但还没下锅,此刻倒掉还来得及。
为何常见:“prepare“(准备)这个词太有迷惑性,初学者容易把它和”commit“(提交)混为一谈,忘了中间还隔着一道”等待指令”的鸿沟。
误区既然 3PC 比 2PC 多了一个阶段,还引入了超时,那它应该是完美方案,可以无脑使用。
正解3PC 显著减小了阻塞窗口和协调者故障的影响,但仍然无法彻底解决网络分区下的脑裂问题。在极端情况下,3PC 依然可能让部分参与者提交、另一部分回滚。它只是把 2PC 的病症“缓解”了,而不是“治愈”了。
为何常见:3PC 名字更高级、流程更复杂,容易让人误以为“多一阶段=更可靠=没问题”。“更多”并不总是等于“更好”。
误区用了本地消息表或事务消息,就觉得消息“必达”等于事务“一致”,把最终一致和强一致画上等号。
正解本地消息表和事务消息只保证“最终一致”,中间存在一段很长的不一致窗口(可能从几十毫秒到数秒)。对资金划拨这类要求强一致的场景,这些方案是不能接受的。强一致只能靠 2PC、TCC 这类同步协调方案来逼近。
为何常见:“消息必达”四个字很有迷惑性,让人误以为“消息到了”就“一致了”,忽略了时间窗口和状态收敛的概念。
误区教科书里先讲 2PC,就觉得它是分布式事务的标准答案,团队里用 2PC 就是“正规军”。
正解2PC 的同步阻塞和单点故障决定了它只适合“参与者少、事务短、并发可控”的场景。在高并发大流量的互联网系统中,2PC 往往成为性能瓶颈,业界更多使用 TCC、Saga、事务消息等替代方案。选型要看场景,而非看名气。
为何常见:2PC 是不少教材里的“第一个分布式事务协议”,先入为主的印象让学习者把它当成了默认首选,忽略了它在高并发场景下的水土不服。
面试题
Q1. 两阶段提交 2PC 中,协调者的核心职责是什么?
- A.统一执行本地数据库事务
- B.发起投票并完成全局决策← 正确答案
- C.仅负责记录操作日志文件
- D.与客户端建立网络连接
答案:B。协调者像年夜饭的总指挥,负责发起 PREPARE 投票、收集各方意见、依据投票结果做出全局 COMMIT 或 ABORT 的决策。参与者才负责执行本地事务,A 错;日志记录虽然重要但只是协调者工作的一部分,C 太片面;与客户端建连不是协调者的核心职责,D 错。
Q2. 2PC 的准备阶段中,参与者需要做什么?
- A.直接提交本地事务数据
- B.执行本地事务并回复投票← 正确答案
- C.将数据复制到其他副本
- D.等待协调者发送指令
答案:B。准备阶段参与者要执行本地事务操作、写 undo/redo 日志,然后向协调者回复 YES 或 NO,但不真正提交。A 把提交提前到了准备阶段,错误;C 是复制不是事务协调;D 说反了,准备阶段参与者是主动执行方。
Q3. 当任意一个参与者返回 NO 时,2PC 协调者的决策是什么?
- A.宣布整体事务提交成功
- B.广播中止并回滚所有参与← 正确答案
- C.忽略该节点的异常信号
- D.重新向该节点发送询问
答案:B。2PC 的提交条件是“全员 YES”,只要有一票反对,全局决策就是 ABORT,协调者广播中止指令,所有参与者回滚。A 无视了反对票;C 忽略异常会破坏一致性;D 的重试机制不属于经典 2PC 的标准流程。
Q4. 下列哪项属于 2PC 的主要缺陷?
- A.无法处理本地单机事务
- B.同步阻塞且协调者是单点← 正确答案
- C.只能支持两个参与者节点
- D.需要额外数据压缩策略
答案:B。2PC 的两个著名缺陷就是同步阻塞(参与者持有锁等待全局指令)和协调者单点故障(协调者宕机全事务悬挂)。2PC 支持任意数量的参与者,C 错;本地事务本就不是 2PC 的职责,A 错;数据压缩与 2PC 无关,D 错。
Q5. 相比 2PC,3PC 最主要的改进是什么?
- A.增加了一轮预提交流程← 正确答案
- B.完全取消了协调者角色
- C.引入密码学验证机制
- D.参与者不再执行回滚
答案:A。3PC 在 2PC 的两阶段之间插入了一个 PreCommit(预提交)阶段,同时为参与者和协调者都引入了超时机制,降低了阻塞窗口。3PC 仍然有协调者,B 错;它与密码学无关,C 错;回滚机制仍然保留,D 错。
Q6. 本地消息表方案的核心思想是什么?
- A.把发消息与本地事务绑定在一起← 正确答案
- B.用人工消息队列替代数据库引擎
- C.提高网络传输的数据压缩率
- D.在多个节点间同步完整日志
答案:A。本地消息表把“业务写入数据库”和“记录待发送消息”放在同一个本地事务中,保证有业务操作就有消息记录,再由后台任务异步发送并重试。它不替代数据库,B 错;与压缩无关,C 错;也不在多节点间同步日志,D 错。
Q7. 2PC 能进入提交阶段的前提条件包括哪些?
- A.所有参与者都返回 YES← 正确答案
- B.协调者自身运行状态正常← 正确答案
- C.网络传输延迟必须为零
- D.参与者持有全部资源锁
答案:AB。提交前提是“全体 YES 且协调者健康”。网络延迟不可能为零,C 错;持有资源锁是准备阶段的动作,不是提交的判定条件,D 错。只有 A(全员赞成)和 B(协调者正常决策)才是进入提交阶段的前提。
Q8. TCC 模式的三个阶段分别是什么?
- A.预备与确认和取消三阶段← 正确答案
- B.准备提交与回滚三阶段
- C.保存更新与删除三阶段
- D.开启结束与后退三阶段
答案:A。TCC 是 Try(尝试预留)、Confirm(确认执行)、Cancel(取消补偿)三个阶段的缩写。B 是 2PC 的准备/提交/中止;C 和 D 是 SQL 或事务的通用词汇,均非 TCC 的标准命名,故排除。
Q9. 关于 2PC 的故障恢复机制,下列说法正确的有哪些?
- A.参与者准备阶段需写入日志← 正确答案
- B.协调者重启后可重发指令← 正确答案
- C.网络分区必然导致数据丢失
- D.参与者必须无限期等待协调者
答案:AB。参与者写 undo/redo 日志是恢复的依据,A 正确;协调者把全局决定记入日志,重启后可据此重发 COMMIT/ABORT,B 正确。网络分区不一定导致数据丢失,只是可能导致不一致窗口,C 过度绝对化;经典 2PC 中参与者确实会长期等待,但这不是“恢复机制”而是一个缺陷,D 描述本身也不构成正确做法。
Q10. 某高并发电商系统需要同时冻结并扣减库存与余额,对一致性要求较高且并发量极大,推荐选择哪种方案?
- A.经典两阶段提交 2PC 协议
- B.TCC 或事务消息方案← 正确答案
- C.仅使用单机本地事务处理
- D.直接关闭一致性约束条件
答案:B。高并发场景下,经典 2PC 的同步阻塞会成为瓶颈,A 不推荐;单机事务无法跨库,C 错;关闭一致性是自毁长城,D 错。TCC 通过 Try 阶段冻结资源、Confirm/Cancel 异步化最终决策,事务消息则允许最终一致,都能更好地支撑高并发,所以选 B。