文章目录

  • 明确问题
  • 草稿方案
  • 原子提交协议希望实现的2个特性
  • 正确的二段提交协议(Two-Phase Commit)
  • 二阶段提交协议如何满足安全性(Safety)
  • 二阶段提交协议如何满足存活性( Liveness)
    • 如何应对超时
      • 超时终止协议
    • 如何应对宕机重启
  • 二阶段提交实现的工程化难点
    • 准备阶段到底干了什么
    • 事务协调者是第三方吗
  • 二阶段提交协议(Two-Phase Commit)的总结

明确问题

从一个场景入手, 假设一个人要从 A 银行向 B 银行进行跨行转账 100 元。

此时我们需要对 A 银行数据库中该用户的账户,做金额扣减操作( - 100), 同时对 B 银行数据库中该用户的账户做金额增加操作 ( +100)

这两个操作( -100 和 + 100) 我们希望它们是一个事务, 要么同时成功, 要么同时失败 。

此时我们的目标是希望有一个

  • 原子性的提交协议 (Atomic Commit Protocol)

草稿方案

  • 场景难点一:

    • A --> B : 你提交我就提交
    • B 不理 A
    • 然后 ?
    • A 和 B 都没有办法继续

先提出一种拍脑门方案, 设置一个事务协调者( Transaction Coordinator)。

由该协调者分别向 A 银行 , B银行发送指令, 发送完毕后直接予以返回成功

显然, 上述方案会出现很多问题:

  • 问题一: A 银行余额不足

    • A 未提交事务, B 却成功提交
  • 问题二: B 银行账户不存在
    • A 提交事务, B 未提交
  • 问题三: TC 到 B 中间的网络发生中断
    • A 提交, B 没提交
  • 问题四: TC 在给 B 发送指令前宕机
    • A 提交, B 没提交

原子提交协议希望实现的2个特性

由上面的场景, 我们可以总结出所有原子提交协议希望实现的2个特性

  • 安全性(Safety)

    • 如果任意一方 commit, 所有人必须都 commit
    • 如果任意一方中断,则没有任何一个人进行 commit
  • 存活性(Liveness)
    • 没有宕机或失败发生时, A 和 B 都能提交, 则提交
    • 如果发生失败时,最终能达成一个一致性结果(成功/失败), 予以响应, 不能一直等待

正确的二段提交协议(Two-Phase Commit)

#mermaid-svg-QmVDYZ6O2HbkDYfz .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .label text{fill:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .node rect,#mermaid-svg-QmVDYZ6O2HbkDYfz .node circle,#mermaid-svg-QmVDYZ6O2HbkDYfz .node ellipse,#mermaid-svg-QmVDYZ6O2HbkDYfz .node polygon,#mermaid-svg-QmVDYZ6O2HbkDYfz .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-QmVDYZ6O2HbkDYfz .node .label{text-align:center;fill:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .node.clickable{cursor:pointer}#mermaid-svg-QmVDYZ6O2HbkDYfz .arrowheadPath{fill:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-QmVDYZ6O2HbkDYfz .flowchart-link{stroke:#333;fill:none}#mermaid-svg-QmVDYZ6O2HbkDYfz .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-QmVDYZ6O2HbkDYfz .edgeLabel rect{opacity:0.9}#mermaid-svg-QmVDYZ6O2HbkDYfz .edgeLabel span{color:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-QmVDYZ6O2HbkDYfz .cluster text{fill:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-QmVDYZ6O2HbkDYfz .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-QmVDYZ6O2HbkDYfz text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-QmVDYZ6O2HbkDYfz .actor-line{stroke:grey}#mermaid-svg-QmVDYZ6O2HbkDYfz .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .sequenceNumber{fill:#fff}#mermaid-svg-QmVDYZ6O2HbkDYfz #sequencenumber{fill:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz #crosshead path{fill:#333;stroke:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .messageText{fill:#333;stroke:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-QmVDYZ6O2HbkDYfz .labelText,#mermaid-svg-QmVDYZ6O2HbkDYfz .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-QmVDYZ6O2HbkDYfz .loopText,#mermaid-svg-QmVDYZ6O2HbkDYfz .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-QmVDYZ6O2HbkDYfz .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-QmVDYZ6O2HbkDYfz .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-QmVDYZ6O2HbkDYfz .noteText,#mermaid-svg-QmVDYZ6O2HbkDYfz .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-QmVDYZ6O2HbkDYfz .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-QmVDYZ6O2HbkDYfz .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-QmVDYZ6O2HbkDYfz .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-QmVDYZ6O2HbkDYfz .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-QmVDYZ6O2HbkDYfz .section{stroke:none;opacity:0.2}#mermaid-svg-QmVDYZ6O2HbkDYfz .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-QmVDYZ6O2HbkDYfz .section2{fill:#fff400}#mermaid-svg-QmVDYZ6O2HbkDYfz .section1,#mermaid-svg-QmVDYZ6O2HbkDYfz .section3{fill:#fff;opacity:0.2}#mermaid-svg-QmVDYZ6O2HbkDYfz .sectionTitle0{fill:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .sectionTitle1{fill:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .sectionTitle2{fill:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .sectionTitle3{fill:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-QmVDYZ6O2HbkDYfz .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-QmVDYZ6O2HbkDYfz .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-QmVDYZ6O2HbkDYfz .grid path{stroke-width:0}#mermaid-svg-QmVDYZ6O2HbkDYfz .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-QmVDYZ6O2HbkDYfz .task{stroke-width:2}#mermaid-svg-QmVDYZ6O2HbkDYfz .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-QmVDYZ6O2HbkDYfz .taskText:not([font-size]){font-size:11px}#mermaid-svg-QmVDYZ6O2HbkDYfz .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-QmVDYZ6O2HbkDYfz .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-QmVDYZ6O2HbkDYfz .task.clickable{cursor:pointer}#mermaid-svg-QmVDYZ6O2HbkDYfz .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-QmVDYZ6O2HbkDYfz .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-QmVDYZ6O2HbkDYfz .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-QmVDYZ6O2HbkDYfz .taskText0,#mermaid-svg-QmVDYZ6O2HbkDYfz .taskText1,#mermaid-svg-QmVDYZ6O2HbkDYfz .taskText2,#mermaid-svg-QmVDYZ6O2HbkDYfz .taskText3{fill:#fff}#mermaid-svg-QmVDYZ6O2HbkDYfz .task0,#mermaid-svg-QmVDYZ6O2HbkDYfz .task1,#mermaid-svg-QmVDYZ6O2HbkDYfz .task2,#mermaid-svg-QmVDYZ6O2HbkDYfz .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-QmVDYZ6O2HbkDYfz .taskTextOutside0,#mermaid-svg-QmVDYZ6O2HbkDYfz .taskTextOutside2{fill:#000}#mermaid-svg-QmVDYZ6O2HbkDYfz .taskTextOutside1,#mermaid-svg-QmVDYZ6O2HbkDYfz .taskTextOutside3{fill:#000}#mermaid-svg-QmVDYZ6O2HbkDYfz .active0,#mermaid-svg-QmVDYZ6O2HbkDYfz .active1,#mermaid-svg-QmVDYZ6O2HbkDYfz .active2,#mermaid-svg-QmVDYZ6O2HbkDYfz .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-QmVDYZ6O2HbkDYfz .activeText0,#mermaid-svg-QmVDYZ6O2HbkDYfz .activeText1,#mermaid-svg-QmVDYZ6O2HbkDYfz .activeText2,#mermaid-svg-QmVDYZ6O2HbkDYfz .activeText3{fill:#000 !important}#mermaid-svg-QmVDYZ6O2HbkDYfz .done0,#mermaid-svg-QmVDYZ6O2HbkDYfz .done1,#mermaid-svg-QmVDYZ6O2HbkDYfz .done2,#mermaid-svg-QmVDYZ6O2HbkDYfz .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-QmVDYZ6O2HbkDYfz .doneText0,#mermaid-svg-QmVDYZ6O2HbkDYfz .doneText1,#mermaid-svg-QmVDYZ6O2HbkDYfz .doneText2,#mermaid-svg-QmVDYZ6O2HbkDYfz .doneText3{fill:#000 !important}#mermaid-svg-QmVDYZ6O2HbkDYfz .crit0,#mermaid-svg-QmVDYZ6O2HbkDYfz .crit1,#mermaid-svg-QmVDYZ6O2HbkDYfz .crit2,#mermaid-svg-QmVDYZ6O2HbkDYfz .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-QmVDYZ6O2HbkDYfz .activeCrit0,#mermaid-svg-QmVDYZ6O2HbkDYfz .activeCrit1,#mermaid-svg-QmVDYZ6O2HbkDYfz .activeCrit2,#mermaid-svg-QmVDYZ6O2HbkDYfz .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-QmVDYZ6O2HbkDYfz .doneCrit0,#mermaid-svg-QmVDYZ6O2HbkDYfz .doneCrit1,#mermaid-svg-QmVDYZ6O2HbkDYfz .doneCrit2,#mermaid-svg-QmVDYZ6O2HbkDYfz .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-QmVDYZ6O2HbkDYfz .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-QmVDYZ6O2HbkDYfz .milestoneText{font-style:italic}#mermaid-svg-QmVDYZ6O2HbkDYfz .doneCritText0,#mermaid-svg-QmVDYZ6O2HbkDYfz .doneCritText1,#mermaid-svg-QmVDYZ6O2HbkDYfz .doneCritText2,#mermaid-svg-QmVDYZ6O2HbkDYfz .doneCritText3{fill:#000 !important}#mermaid-svg-QmVDYZ6O2HbkDYfz .activeCritText0,#mermaid-svg-QmVDYZ6O2HbkDYfz .activeCritText1,#mermaid-svg-QmVDYZ6O2HbkDYfz .activeCritText2,#mermaid-svg-QmVDYZ6O2HbkDYfz .activeCritText3{fill:#000 !important}#mermaid-svg-QmVDYZ6O2HbkDYfz .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-QmVDYZ6O2HbkDYfz g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-QmVDYZ6O2HbkDYfz g.classGroup text .title{font-weight:bolder}#mermaid-svg-QmVDYZ6O2HbkDYfz g.clickable{cursor:pointer}#mermaid-svg-QmVDYZ6O2HbkDYfz g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-QmVDYZ6O2HbkDYfz g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-QmVDYZ6O2HbkDYfz .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-QmVDYZ6O2HbkDYfz .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-QmVDYZ6O2HbkDYfz .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-QmVDYZ6O2HbkDYfz .dashed-line{stroke-dasharray:3}#mermaid-svg-QmVDYZ6O2HbkDYfz #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-QmVDYZ6O2HbkDYfz #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-QmVDYZ6O2HbkDYfz #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-QmVDYZ6O2HbkDYfz #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-QmVDYZ6O2HbkDYfz #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-QmVDYZ6O2HbkDYfz #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-QmVDYZ6O2HbkDYfz #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-QmVDYZ6O2HbkDYfz #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-QmVDYZ6O2HbkDYfz .commit-id,#mermaid-svg-QmVDYZ6O2HbkDYfz .commit-msg,#mermaid-svg-QmVDYZ6O2HbkDYfz .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-QmVDYZ6O2HbkDYfz .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-QmVDYZ6O2HbkDYfz .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-QmVDYZ6O2HbkDYfz g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-QmVDYZ6O2HbkDYfz g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-QmVDYZ6O2HbkDYfz g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-QmVDYZ6O2HbkDYfz g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-QmVDYZ6O2HbkDYfz g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-QmVDYZ6O2HbkDYfz .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-QmVDYZ6O2HbkDYfz .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-QmVDYZ6O2HbkDYfz .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-QmVDYZ6O2HbkDYfz .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-QmVDYZ6O2HbkDYfz .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-QmVDYZ6O2HbkDYfz .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-QmVDYZ6O2HbkDYfz .edgeLabel text{fill:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-QmVDYZ6O2HbkDYfz .node circle.state-start{fill:black;stroke:black}#mermaid-svg-QmVDYZ6O2HbkDYfz .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-QmVDYZ6O2HbkDYfz #statediagram-barbEnd{fill:#9370db}#mermaid-svg-QmVDYZ6O2HbkDYfz .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-QmVDYZ6O2HbkDYfz .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-QmVDYZ6O2HbkDYfz .statediagram-state .divider{stroke:#9370db}#mermaid-svg-QmVDYZ6O2HbkDYfz .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-QmVDYZ6O2HbkDYfz .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-QmVDYZ6O2HbkDYfz .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-QmVDYZ6O2HbkDYfz .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-QmVDYZ6O2HbkDYfz .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-QmVDYZ6O2HbkDYfz .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-QmVDYZ6O2HbkDYfz .note-edge{stroke-dasharray:5}#mermaid-svg-QmVDYZ6O2HbkDYfz .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-QmVDYZ6O2HbkDYfz .error-icon{fill:#522}#mermaid-svg-QmVDYZ6O2HbkDYfz .error-text{fill:#522;stroke:#522}#mermaid-svg-QmVDYZ6O2HbkDYfz .edge-thickness-normal{stroke-width:2px}#mermaid-svg-QmVDYZ6O2HbkDYfz .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-QmVDYZ6O2HbkDYfz .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-QmVDYZ6O2HbkDYfz .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-QmVDYZ6O2HbkDYfz .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-QmVDYZ6O2HbkDYfz .marker{fill:#333}#mermaid-svg-QmVDYZ6O2HbkDYfz .marker.cross{stroke:#333}:root { --mermaid-font-family: "trebuchet ms", verdana, arial;}#mermaid-svg-QmVDYZ6O2HbkDYfz {color: rgba(0, 0, 0, 0.75);font: ;}

TC 发送 “prepare” 给 A 和 B
等待响应结果
A 和 B 均返回 Yes
A 和 B 任意一个返回 No
TC 向 A 和B 发送 commit 指令, 向 client 发送 ok 响应
TC 向 A 和B 发送 aboort 指令
A 和 B 在收到 TC 发送的 commit 指令后, 执行 commit 操作

上面的流程看似简单, 但是有一个点容易被忽略:

  • 当 TC 收到 A 和 B 响应 “Yes” 后, 做出了 “commit” 的决定, 向 A /B 发送指令或, 并不需要再等待 A/B 的响应, 可以直接向 client 返回成功
  • 之所以可以这样做的原因是, 当 A/B 返回 Yes 后, 就代表 A 和 B 都做好了提交准备, 只要 TC 决定要提交,即使 A/B 宕机, 没有收到 TC 的 Commit 指令, 只要 A/B 被修复重启, A 和 B 都必须有能力成功完成提交操作。

二阶段提交协议如何满足安全性(Safety)

  • 事务协调者 TC,作为一个中心, 统一收集了 A 和 B 是否有意愿(有能力)进行 commit
  • 事务协调者 TC 强制保证了, A, B 双方必须都有意愿提交时, 才进行 commit

二阶段提交协议如何满足存活性( Liveness)

遗憾的是: 上面描述的协议无法满足存活性。

下面分析一下这个协议在执行过程中可能面临哪些问题

  • 问题1: 响应超时

    • 结点正常运行, 但是没有正常收到它所期待的响应, 可能原因如下

      • 其他的结点故障了
      • 网络情况不好, 数据包丢失了或网络干脆中断了
  • 问题2: 重启
    • 结点宕机, 重启以后, 如何恢复被中断的操作

如何应对超时

首先分析整个协议里面有哪些等待操作?

  1. 事务协调者 TC 需要等待 A 和 B 返回 “yes”/“no” 才能进行下一步操作
  2. A 和 B 需要等待 TC 发送 “commit”/ “abort” 指令, 才能进行下一步操作

然后分析等待状态超时的时候, 是否有办法继续协议

  • 事务协调者 TC 需要等待 A 和 B 返回 “yes”/“no” 超时:

    • 此时 , TC 还没有发送过任何 “commit” 指令
    • TC 此时可以安全地发起终止 “abort” 指令, 放弃 commit
      • 上面这种做法, 保证了安全性, 放弃了存活性
      • 因为 A, B 可能都做好了准备进行提交, 只是 “yes” 信息没有成功被 TC 收到, 就导致了整个事务无法提交
      • 这种情况属于本可以提交而未提交, 也就是说 TC 采取了非常保守的方案
  • A 和 B 需要等待 TC 发送 “commit”/ “abort” 指令 超时:

    • 以 B 为例进行考虑( A 的情形完全对称)
    • 如果 B 之前回复的是 “no” , 那此时, B 可以无需等待 TC 回复就放弃 commit 操作, 因为 TC 即使收到了响应, 也会回复 “abort”, 这个行为是统一的
    • 如果 B 之前回复了 “Yes”,那此时 B 能单方面地直接进行 aboort 操作吗?
      • 不行! 因为, TC 此时可能已经成功收到了 A, B 返回的 “Yes”, 并且已经向 A 发送了 “Commit”, 然后再向 B 发送 “commit” 前宕机了
      • 如果 B 放弃了 commit 操作, 就会出现 A 执行了 commit, B 未执行 Commit 的情形, 显然违背了安全性(Safety)
    • 那 B 能单方面地直接进行 commit 操作吗?
      • “不行” ! 因为 A 可能返回给TC 的响应是 “No”
    • 那此时应该怎么办?:
      • 方案一: B 一直等待 TC 的 “commit”/ “abort” 指令
      • 方案二(更好): B 针对这种情形发起一轮终止协议操作(Termination Protocol)

超时终止协议

  • B 向 A 发送状态查询请求, 询问 A 是否知道事务已经提交
  • 如果 B没有收到 A 的响应, B 无法进行后续操作, 只能继续等待
  • 如果 B 收到了 A 的响应, 则分如下几种情况:
    • A 回复说 , 它已经收到了来自 TC 的 “commit”/ “abort” 指令

      • 此时 B 可以执行 “commit”/ “abort”, 应为 TC 发给 B 的指令肯定和 A 一样
    • A 回复说, 它还没有向 TC 回复 “yes”/“no”,
      • 此时 B 和 A 都直接执行 abort 操作
      • 不必担心 TC, 因为 TC 尚未收到 A 的回复, 最终会根据 A 和B 的状态回复 client
    • A 回复说, 它向 TC 回复了 “no”
      • 此时 B 和 A 都直接执行 abort 操作
    • A 回复说, 它向 TC 回复了 “yes”
      • 此时 B 不能进行后续操作
      • 因为 TC 可能已经收到了 A 和 B 的 “Yes” 响应, 并且决定执行 “commit”, 向 A 和 B 发送了“commit” 指令, 只是没被 A 和 B 收到, 但是 TC 发送 “commit” 之后就会直接向客户端返回了 “ok”
      • TC 也有可能在等待 A 和 B 的响应过程中超时了, 直接进行了 “abort” 决定, 向 A 和 B 发送了 “abort” 指令, 只是没被 A 和 B 收到, 但是 TC 发送 “abort” 之后就会直接向客户端返回了 “fail”

如何应对宕机重启

基本原则: 一旦 TC 决定了 commit , 那么任意一个结点都不允许发生回滚

有如下几种情形需要考虑:

  • TC 在做出决定后, 立即宕机了, 没有吧 commit 指令成功发送给 A 和 B , 然后被重新启动
  • A 和/或 B 在发送 Yes 的过程中, 宕机了, 没有把 " Yes" 成功发送给 TC , 然后被重新启动

如果所有的结点, 都能知道他们在宕机前的状态是什么, 那就有如下几种解决方案:

  • A 或 B 相互发起之前描述的终止协议 Termination Protocol, 即相互询问是否知道事务已经交
  • A 和 B 也可以向 TC 发起状态查询操作, TC 可能知道事务是否已经提交

如何保证在宕机重启后, 依旧能够记得宕机前的状态:

  • 在发送任何信息给其他结点前, 一定要先行将自己要回复的内容写入磁盘, 这样可以保证一旦宕机, 可以知道宕机前的状态。

    • 对于 TC 而言, 在向 A 和 B 发送 “commit” 指令前, 一定要先行将 “commit” 成功记录到磁盘
    • 对于 A/B 而言, 在向 TC 发送 “yes” 之前, 一定要先行将 “yes” 记录成功记录到磁盘

这样重启之后就可以进行如下的操作:

  • 对于TC, 重启以后, 如果发现磁盘中没有记录 “commit” , 那就可以直接进行 “abort” 操作

    • 磁盘中没有 “commit” , 就说明根本没向 A/B 发送过 “commit”, 此时是安全的
  • 对于 A 或者 B , 重启以后, 如果发现磁盘中没有记录 “yes” , 那就可以直接进行 “abort” 操作
    • 磁盘中没有 “yes” , 说明根本没有向 TC 发送过 “yes”, TC 不可能做出 “commit” 操作, 执行abort 是安全的
  • 对于 A或者 B, 重启以后, 如果发现磁盘中有 “yes” 记录, 那就可以发起终止协议 “termination protocol”
    • 终止协议有可能陷入继续的等待中
  • 如果TC, A, B 都发生了重启操作, 只要当 3 个结点都恢复以后, 就可以向 TC 发起查询, 查看TC 的磁盘中是否存在 “commit” 记录, 如果存在, 则均可进行 “commit” 操作

二阶段提交实现的工程化难点

准备阶段到底干了什么

回顾之前的内容,可以发现, 二阶段提交协议(Two-Phase Commit)的核心是

  • 引入了一个事务协调者(TC)
  • 在真正的提交操作前, 增加了一个准备阶段, 收集业务结点是否有能力进行提交

部分程序员可能会对其工程实现的一个关键点产生误解:

  • 准备阶段就是开启一个事务, 执行所有的业务代码, 都不报错, 不执行事务的 commit 操作, 然后向 TC 回复 “Yes”, 表示我已准备好提交

这种做法并不满足二阶段提交协议对于准备操作的要求:

  • 二阶段提交协议中, 业务结点回复 “Yes” , 代表它做好了提交操作的所有准备, 只要结点回复了 “Yes”, 即使突然发生宕机, 只要结点重新启动, 收到了 TC 发送的 commit 指令, 必须依旧能正确提交
  • 普通数据库如果在在一个事务中间发生了宕机(比如数据库所在机器直接停电), 重启以后, 数据库的默认行为是对处于中间状态的事务进行回滚操作, 并不具备继续等待并接受 commit 指令的能力

以之前提到的转账操作为例, 如果 A 银行需要对转账客户的账户执行 -100 元操作, 当它向 TC 回复了 “Yes” 前, 应该完成以下操作:

  • 确定账户上有 100 待扣减, 将这100 元冻结, 其他的操作无法解冻, 转移这100元。
  • 留下必要的持久化记录, 确保即使宕机重启, 收到 “abort” 指令也有能力回滚到100 元被冻结前的状态
  • 留下必要的持久化记录, 确保即使宕机重启, 收到 “commit” 指令也有能力正确提交, 完成 -100 元操作
  • 留下必要的持久化记录, 标识自己已经完成了准备阶段的所有操作, 要向 TC 回复 “Yes” 指令

只有以上这些操作都成功完成以后, 银行 A 才能尝试向 TC 发送 “Yes” 指令,否则就有违二阶段提交协议

事务协调者是第三方吗

二阶段提交中, 除了准备阶段, 另一个显眼的角色就是事务协调者( Transaction Coordinator)。

此时就会有部分同学产生困惑, 事务协调者是否一定得是一个独立的机器,处于独立的结点。

答案: 并不是!

从准备阶段的要求就可以看出。 二阶段提交协议的核心, 是描述了在每一步操作前, 每一种角色应该达到什么状态, 具备什么能力。 具体在工程实现中, 这几种角色分布在几个结点, 以什么方式去实现, 都是可以的。

以前文所举的银行转账操作为例。

客户端 client 发起一个转账操作请求给 TC, 这个 TC 完全可以就属于银行 A。 只要银行 A 实现的 TC 在银行 A 数据库发起 -100 元的操作前,依旧先按照协议要求;

  • 模拟 prepare 阶段, 进行持久化记录
  • 做好 -100 元随时提交或回退的准备

即可以根据银行 B 的准备阶段应答结果, 进行后续的操作。

二阶段提交协议(Two-Phase Commit)的总结

二阶段提交所说的二阶段分别指: Prepare , Commit 两个阶段

二阶段提交满足如下性质:

  • 安全性: 所有的结点最终会达成一致的决定
  • 安全性: 只有当所有人都说了 “yes”, 才会执行提交
  • 存活性: 如果没有宕机和信息丢失, 提交肯定可以完成
  • 存活性: 如果发生宕机和信息丢失, 只要最终修复, 并且重启, 等待足够长的时间, 最终一定可以达成某项一致的结果(abort 或者 commit)

可能有同学会有疑问, 那如果宕机不能被修复, 那些处于中间状态的记录怎么办:

1985 年 Fischer, Lynch, Paterson 提出了定理:

no distributed asynchronous protocol can correctly agree in presence of crash-failures

翻译一下就是

在出现宕机时(最终没有修复并重启), 不存在一种分布式的异步协议可以正确地达成一致结果(同时提供安全性和存活性)。

所以有故障发生, 不修复还想保证记录达成一致? 洗洗睡吧

正确理解二阶段提交(Two-Phase Commit)相关推荐

  1. 分布式事务理论-二阶段提交(Two-phase Commit)

    1 2PC 两阶段提交协议为了保证分布在不同节点上的分布式事务的一致性,我们需要引入一个协调者来管理所有的节点,负责各个本地资源的提交和回滚,并确保这些节点正确提交操作结果,若提交失败则放弃事务. 2 ...

  2. java 二阶段提交,二阶段提交协议(Two Phase Commitment Protocol)

    一.典型的分布式事务实例 跨行转账问题是一个典型的分布式事务,用户A向B的一个转账1000,要进行A的余额-1000,B的余额+1000,显然必须保证这两个操作的事务性. 类似的还有,电商系统中,当有 ...

  3. 关于2PC(二阶段提交)和3PC(三阶段提交)的理解

    分布式系统和分布式一致性问题 分布式系统,即运行在多台不同的网络计算机上的软硬件系统,并且仅通过消息传递来进行通信和协调. 分布式一致性问题,即相互独立的节点之间如何就一项决议达成一致的问题. 2PC ...

  4. 分布式理论 二阶段提交 2PC 3PC 端到端一致性 分布式事务

    一.临界知识对我们学习的巨大帮助 临界知识这个概念,是我上个月读<好好学习:个人知识管理精进指南>这本书学到的概念,真的有被启发到,现在觉得它对于我们深刻了解世界有着非常大的作用. 所谓临 ...

  5. 两阶段提交(Two-Phase Commit)

    两阶段提交 两阶段提交是一种同步协议,是计算机网络尤其是在数据库领域内,为了使基于分布式系统架构下的所有节点在进行事务处理过程中保持原子性和一致性而设计的一种算法. 两阶段提交的执行过程 在两阶段提交 ...

  6. mysql三阶段提交_二阶段提交和三阶段提交

    一.2PC 2PC即两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Prepare phase).提交阶段(commit phase),2是指两个阶段,P是指准备阶段,C是指提交阶段 整个事务 ...

  7. 二阶段提交,三阶段提交,Paxos

    本文转载自:http://blog.csdn.net/u010191243/article/details/52081891 http://blog.chinaunix.net/uid-1672327 ...

  8. 漫话:如何给女朋友解释什么是2PC(二阶段提交)?

    周末晚上,正在家里面看综艺节目,突然女朋友跑过来找我打<王者荣耀>. 打了几把游戏,终于可以歇息一会了,准备继续看我的综艺,可是女朋友过来找我给他讲讲到底什么是二阶段提交. 分布式一致性 ...

  9. 漫画:如何给女朋友解释什么是2PC(二阶段提交)?

    作者|漫话编程 本文经授权转载自漫话编程(ID:mhcoding) 周末晚上,正在家里面看综艺节目,突然女朋友跑过来找我打<王者荣耀>. 打了几把游戏,终于可以歇息一会了,准备继续看我的综 ...

最新文章

  1. Pandas基本操作指南-2天学会pandas
  2. 在线服务器和客户端聊天,实验三、客户端和服务器能实现简单的聊天功能
  3. 为什么我不推荐你盲目追求微服务?迟早要吃亏!
  4. c语言如何输出一维数组字母,C语言一维数组初步学习笔记
  5. 关于Adodb.Stream 的使用说明
  6. python21天打卡Day12--for循环,列表推导式-构建列表
  7. 【WPS表格】快捷键合集(更新中)
  8. 公文排版插件for Word/WPS【快点公文助手——让公文排版更快一点】
  9. stm32看门狗定时器记录
  10. 李大仁是真的爱程又青
  11. git 一个分支完全覆盖另一个分支
  12. 厦理期末给爷过-Python
  13. Laravel-日志管理
  14. 用open3d将stl格式转化为ply格式,并且单位从mm转为m
  15. 机器编曲准备--爬虫
  16. 玩转Java高并发?请先说明下并发下的惊群效应
  17. 硕博生到底该不该参加学术会议
  18. java pdf 水印_Java在PDF中添加水印(文本/图片水印)
  19. 37岁码农现身说法:大龄程序员找工作
  20. 使用clumsy模拟网络延迟

热门文章

  1. Java实现Redis的发布和订阅
  2. 【Tomcat】解决Tomcat服务器乱码问题
  3. Python变量引用错误
  4. 深入学习setState
  5. lvm lvextend vgdisplay xfs_growfs
  6. JDK7中HashMap源码分析
  7. 下载Windows版本的Redis
  8. latex语法学习(二)
  9. 进阶系列(12)—— C#异步编程
  10. 华为简单的生成树协议配置