5.2 两阶段提交的投票


5.2 两阶段提交(2PC):一场必须有协调者的投票

两阶段提交用一种朴素到极致的办法保证跨节点"全或无":一个协调者先让所有参与者准备(锁资源、写日志、告诉能不能提交),等大家都说"可以",再统一发令提交。本节走一遍这场投票的完整流程,也要毫不留情地指出它的软肋——阻塞、协调者单点、以及那期它最爱出状况的宕机窗口。

学习目标

阅读完本节,你应当能够:

  1. 复述 2PC 的准备(投票)与提交两个阶段,说明各阶段发生了什么。
  2. 指出 2PC 在"参与者已准备、协调者却宕机"时为什么憋住不动。
  3. 判断 2PC 适合哪类短小、参与者少、对一致要求硬核的业务。

先想清楚:跨节点"全或无"凭什么难

下单这种小事,往往要动订单表、库存表、账户表,还可能分在几个节点上。单机里保证"全或无"靠本地日志和锁;可一旦跨节点,难题就浮现了——如果只给节点 A 发了提交而没来得及发给 B,B 那边到底要不要改? 改了一半最糟。你需要一个所有人都认可的发号施令者,让"发到一半"这种半吊子状态不会发生。这就是两阶段提交的设计动机。

一、两阶段的完整流程

第一阶段·准备(投票):协调者把事务请求发给所有参与者。每个参与者执行自己该做的写入并记录日志,然后先把资源锁住、把改动做成"待提交"状态,最后向协调者回一句"我准备好了,可以提交"或"我准备不了,要回滚"。注意,这阶段修正一下常见误解:参与者不是真的提交,只是"承诺能提交"。

第二阶段·提交:协调者收集所有参与者的投票。只要全员都答"可以",协调者就下发"请提交";只要有一个答"不"或超时没回,协调者就下发"请回滚"。参与者收到指令后各自落定,向协调者确认。

两阶段提交的消息时序

二、三条绕不开的软肋

2PC 能给出最刚的"全或无",但代价是三条硬伤,工程里必须心里有数:

一是阻塞。在全员都没回复的可忍耐窗口里,那些被锁住的资源谁也抢不了。只要有一个参与者迟迟不点头,整个事务就挂在那,被它锁着的行,别人连读都困难(取决于隔离级别)。它慢时是真正的慢,跟你的操作数成比例。

二是协调者单点。所有人看的是协调者。协调者要是中途宕了,好事变坏事:参与者已经准备就绪、锁也占着,可没有协调者的命令,既不能提交也不敢回滚,只能傻等。这个"憋住不能动"的状态,是 2PC 最经典的坑,学过协议都该记住它长什么样。

三是锁与延迟放大到全局。2PC 把"一个参与者慢"的影响放大到了整个事务。只要有一个节点响应慢或网络抖动,所有人都陪着等它;参与者的数量一多,这种"最慢者决定速度"的效应就越明显。这也是为什么 2PC 常见于参与者少、事务短小、对强一致要求硬核的场景——它受不了大批量、长事务的拖累。

三、什么时候别碰 2PC

结合三条软肋,很容易画出 2PC 的适用圈:事务短、参与者少、要求"全或无" 的场合,它是直截了当的答案;事务长、常跨大量节点、或对可用性不敏感(可接受短暂不可用) 的场合,就要另想办法。工程里见到最多的两处:一是跨库一致性不容妥协的金融/计费短事务,二是分布式数据库里分布式事务的底层基石(当成"地基里的最后一根钉子"用)。而面向高并发互联网的长事务,2PC 常常进不了门——它的阻塞和单点,正是下一节 3PC 想弥补的、以及再往后 Saga/TCC 想干脆绕开的。

四、把"协调者什么时候死,死出什么后果"背下来

2PC 最被人诟病的地方,全藏在"协调者的死法"里。做一张时刻表,你就再也不会懵:

协调者倒下的时点 参与者的状态 后果与解药
准备阶段还没发齐 还没锁资源 受影响小,几乎无感
准备阶段发了、还没收齐投票 有的已锁资源 锁者受困,只能等协调者恢复或超时判回滚
已经收到全员"可以"、还没下提交令 全员已锁、绝不擅自提交 最经典死局:全员被憋住,靠协调者重启补发
提交令发到一半 有的已提交、有的没收到 出现"部分提交"的脏窗口,要靠日志与再议兜底

结论很清楚:协调者最容易出事、出事影响最大的,恰恰是它最不能出事的那一小段时间——收集完投票到发出提交令之间。 这也是三阶段提交(下一节 5.3)和各类"协调者高可用"(给协调者再做主备、用共识让它不再单点)最想收拾的窗口。

五、一个会算的账:为什么它兜不住长事务

把 2PC 的成本用一个极简例子量化,就明白"短事务专用"不是拍脑袋。假设一个事务要动 4 个参与者、每轮消息往返 20 毫秒、锁平均占用 50 毫秒。2PC 光是准备+提交两轮网络,就至少多出 4 个参与者的往返等待;更要命的是,只要其中一个节点慢到 1 秒,其余所有参与者的锁都要陪着多等近 1 秒。你这边的代价 = 参与者数 × 协调等待 × 最慢者放大系数,参与者和并发一上来,这个乘积立刻失控。所以生产经验极其一致:2PC 只适合事务短、参与者少(个位数)、要求硬强一致的记账、扣款、发券类短事务;一碰到跨十个服务的长链路,就得换 3PC 减阻塞,或干脆上补偿/Saga 绕开。补一句实战经验:如果你已经决定用 2PC,就把"参与者会不会太多、事务会不会拖得太长"这两条当作红线,在方案评审时提前设限——这能有效规避 2PC 最狼狈的那几类现场。

本节要点回顾

  • 两阶段:先"准备投票",全体通过才"提交",否则回滚。
  • 能保全或无:2PC 给出最刚的跨节点原子性。
  • 三条硬伤:阻塞耗资源、协调者单点憋停、最慢者拖累全局。
  • 适用圈:短事务、少参与者、强一致优先,别碰长事务大并发。

2PC 最大的心结是"协调者一宕,大家都被憋住"。三阶段的点子很直接:在准备和提交之间,再插一次"大家能不能提前达成一致"的表态。下一节 5.3 看三阶段提交(3PC)是怎么靠这多出来的一步缓解阻塞的。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U