MIT 6.824拾遺(一)聊聊basic-paxos

KatyuMarisa發表於2021-03-27

前言

The Paxos algorithm, when presented in plain English, is very simple.
—————— Lamport,《Paxos Made Simple》

Unfortunately, Paxos is quite difficult to understand, in spite of numerous attempts to make it more approachable.
—————— Diego Ongaro,《In Search of an Understandable Consensus Algorithm》

在不同的部落格和視訊中,對於basic-paxos的講解有多個維度。例如說zhihu作者@多顆糖和Diego Ongaro(Raft演算法的提出者)的視訊是以具體事例作為角度來討論和分析的,而Lamport的《Paxos Made Simple》和Paxos的Wikipedia則通過逐級新增和強化約束條件的方式,來逐步使一個簡單的演算法漸漸向共識演算法靠攏,最終推匯出basic-paxos的實現邏輯。我個人比較推薦的是首先參照Diego Ongaro和 @多顆糖 給出的註解來學習和了解basic-paxos的基本邏輯,再將自己想到的和鎖提到的各種情景套入到演算法中,推斷這些情景的存在性、產生過程和解決方法,最後閱讀Lamport的論文。

本篇blog更傾向於做一個關於《Paxos Made Simple》中關於basic-paxos的個人註解。參考的資料我會放在部落格的最末尾。

共識問題

共識

在一個系統中包含有n個程式{1,2,3,4...n},部分程式會提出一個VALUE,程式之間互不相知對方的VALUE到底是多少,但可以通過相互通訊來獲悉其他程式的VALUE,也可以考慮修改自己的VALUE;我們需要設計一種演算法,使得這n個程式最終能夠協商達成一個不可撤銷的最終VALUE;能達成這個目的的演算法就被稱作是一種共識演算法

非同步網路條件

程式之間必須通過相互通訊才能獲悉對方的VALUE,但通訊過程是完全非同步的。如果程式1希望與程式2通訊,那麼程式1就要向程式2發出請求,然後等待程式2的答覆;在非同步網路環境下,這個等待的時間可能是無限的。

非拜占庭條件

程式之間的非同步通訊可能丟失、重複、失序、延遲,通訊網路之間也可能會發生分割槽(但分割槽最終會復原),但通訊的內容必定不會損壞(corrupt);
程式本身也隨時可能崩潰、重啟,但程式一旦開始執行,必定處於正確的狀態。在程式間的通訊中,程式的回應必定能正確反映自己的狀態,即程式不會對其他程式進行無意(進入了錯誤狀態但不自知)或者惡意的(被黑客篡改、偽造了通訊內容)欺詐;

對於一個共識演算法來說,它的執行必須滿足如下三個性質:
1)終止性(Termination),所有的程式最終都會認同某一個值;
2)協定性(Agreement),所有程式最終認定的必定是同一個值;
3)完整性(Integrity),如果正確的程式都提議同一個值,那麼所有處於認同狀態的程式都選擇該值;

由於我們假定不存在拜占庭問題,因此只要共識演算法只需滿足1)和2)即可。

共識(consensus)問題是分散式系統研究中的基礎問題之一,雖然與解決了多複製狀態機間狀態一致性問題的raft演算法相比,basic-paxos所解決的共識問題看起來更偏向底層、難以直接應用,但其同樣可以解決分散式系統中的一系列核心問題。

在理解了basic-paxos執行的環境(非同步非拜占庭),以及basic-paxos所需要解決的問題之後(達成共識,並滿足三個共識演算法的性質),我們可以開始討論basic-paxos的實現了。

paxos的基本概念

在一個paxos系統中存在三類角色:proposers(提議者)、acceptors(接收者)、learners(學習者)。由proposer提出VALUE,由acceptor表決是否接受(accept)proposer提議的VALUE,共識(consessus)的達成等價於acceptors們對VALUE達成一致共識,learner只能被動的學習acceptor所達成共識的VALUE;

舉例來說,系統中有2個proposer,5個acceptor,2個learner;我們現在希望系統對Colors = {RED,ORANGE,YELLOW,GREEN,BLUE,BLACK,PURPLE} 中的一種Color達成共識;那麼需要由proposer發出(issue)提議,希望acceptor們能接受(accept)它所提議的Color(兩個proposer可能提議不同的顏色,單獨的一個proposer也可能中途改變主意,發出另一種顏色的提議);當acceptor能夠對COLOR達成共識時,便選出(chosen)了一個不可撤銷的Color(假設為YELLOW);acceptor間達成共識的結果應當被learner所學習到;

等價性

不難理解,前文所述的共識演算法的三點特性,等價於paxos演算法中的以下三個特性:
1)決議(VALUE)必須由proposer提出;
2)如果acceptor達成了共識,那麼它們認定的VALUE不可撤銷
3)learn只能學習到那些達成共識的VALUE;

也就是說,我們paxos演算法只要保障了以上三個特性,便保證了共識演算法需要保證的特性,那麼我們的paxos演算法就是一種共識演算法;這樣將原本比較抽象的問題轉換成了比較具體的問題;我們在上文中又提出了三個新概念:accept、chosen、issue:

accept

accept指一個acceptor暫時接受了一個VALUE,但不保證這個VALUE是最終達成共識的VALUE;當acceptor收到了新的提案時,它可能會修改自己所accept的VALUE。

chosen

當超過半數的acceptor成功的accept了一個VALUE之後,我們稱這個VALUE已經被選中(chosen)了,這個VALUE就是最終必須要達成共識的VALUE,而這也意味著我們可能會修改acceptor所accept的VALUE,讓它變成對應的被chosen的VALUE。

issue

一切的VALUE必須先由proposer釋出(issue),然後才可能被一些acceptor所接受(accept),然後才可能有機會最終被選中(chosen)。如果一個VALUE沒有被proposer所釋出,那麼這個VALUE就不可能被選中(chosen)。

值得注意的是,在Paxos中,一臺機器可以同時擔任三種角色中的多個角色。

在定義了上述內容之後,我們終於可以逐漸推導basic-paxos演算法了。我們首先討論最簡單的情景與解決這一情景的簡單演算法,再逐漸強化我們的演算法以推廣到更加複雜的情景,最終得到basic-paxos演算法。

約束條件P1

Lamport首先假定了一個非常簡單的情景:網路條件良好(請求和應答都是立即送達的)且機器不會崩潰,只issue了一個提案且這個提案只會被髮布一輪,自然也就只有一個VALUE被提出;我們期望我們的演算法在這個情景下必定能chosen那個VALUE;為此引入了約束條件P1:

P1. An acceptor must accept the first proposal that it receives.

容易證明,在上述情景下,如果演算法滿足P1,則必定能chosen一個VALUE,即P1在上述情景下是完備的。但如果把情景稍微複雜化,則P1就不完備了:我們假定多個proposer們issue了提案,這些提案涉及到了不同的VALUE,且存在一定的網路延遲,且proposer們只issue一輪提案;根據P1,acceptor們根據請求到來的先後順序選擇了VALUE,但可能沒有一個VALUE被半數以上的acceptor所accept,即沒有一個VALUE被chosen,如下圖所示:

由於proposer們只會釋出一輪提案,因此acceptor們永遠不會改變自己所accept的VALUE,也就永遠不會有VALUE被chosen,這樣就違背了共識演算法的Termination要求;

提案號(ProposeNum)的引入

通過上述討論我們可以得知,如果僅進行一輪提案,則可能會因為其他proposer與之競爭acceptor導致無法chosen一個VALUE。但上文的情景中,僅僅假定網路只會造成延遲(Delay)而已,更加惡劣的情況(丟失、失序等)還尚未考慮在內。實際上在非同步的情景下,即使一輪提案順利的chosen了一個VALUE,proposer也可能因為acceprot的回覆丟失和延遲而久久無法獲知。因此,約束條件P1僅在前文中的那個簡單的情景下適用。現在我們的情景更加複雜了,我們需要尋找更多的約束條件來使演算法達到要求!

由於一輪提案無法保證chosen一個VALUE,因此我們的basic-paxos需要新增多輪提案的支援。此時我們的演算法希望能夠保證,無論我的提案會進行多少輪,只要我最終能chosen一個不可撤銷的VALUE即可。

為了區分開不同的提案以及不同輪次的提案,我們需要為每一個提案分配一個唯一的、單調遞增的proposeNum,並將這個proposeNum捎帶在通訊的資訊中。最簡單的proposeNum的生成方法為 concatenate(物理or邏輯時鐘,proposerId),即將物理or邏輯時鐘作為proposeNum的高位,將proposerId作為proposeNum的低位。由於物理or邏輯時鐘是嚴格遞增的,因此proposeNum是單調遞增的;由於proposerId是唯一的,因此proposeNum必定是唯一的。至於proposeNum的具體應用,將在下面的內容中進行講解。

上圖為Diego Ongaro在講解Paxos的視訊中使用的ProposerNumber的生成方式,Round Number本身可以看做是一個邏輯時鐘,類似於Raft演算法中的term。

再談約束條件P1

我們回憶一下P1的內容:

P1. An acceptor must accept the first proposal that it receives.

在Lamport為提出P1所舉的例子中,限定只進行一輪提案,因此the first proposal的指代是非常明顯的,但在引入了proposeNum之後,the first proposal的指代就不是很明確了,Lamport在《Paxos Made Simple》中也沒有很明確的定義在引入了proposeNum下的 the first proposal 到底是什麼。

我個人根據上下文的是這樣理解的:假設無法保證proposer們提案的proposeNum不同,那麼存在不同的proposer通過同一個proposeNum提出不同VALUE的情景;對於這些相同proposeNum但不同VALUE的多個提案,acceptor只能accept它所收到的第一個提案,並拒絕相同proposeNum其他的VALUE;但由於proposeNum是全域性唯一、僅用一次的、僅能由唯一的proposer生成的,因此一旦proposeNum給定,那麼這個提案的VALUE就已經確定了,因此不存在前文中我所提到的問題。

到此為止,如果是按照我的理解的話,P1條件已經變得非常模糊甚至可以認為不存在了,因為acceptor接受任何proposeNum下的任何VALUE,仍然是滿足P1的。但這沒有關係,在討論完P2之後,我們會強化P1條件,使其成為我們共識演算法的重要組成部分。

約束條件P2

上文長篇大論的討論的內容濃縮起來其實很簡單:在非同步、非拜占庭條件下,共識可能需要一輪以上的提議才能實現,但多輪提議僅僅是共識演算法的一個必要不充分條件,我們還需要加入更強的約束。

引入了多輪提議之後,你可以嘗試著去構想一些情景,你會發現,構建一個多輪提議下仍然無法讓半數以上的acceptor們accept同一個VALUE的情景,實在是太容易太容易了。怎麼避免他們陷入這種無窮無盡的扯皮,暫時不是我們要考慮的內容,我們現在要考慮的是另一個同等重要的問題:如何保障chosen VAUE不可撤銷,或者說能避免chosen到第二個VALUE?

我們知道,一個VALUE被chosen等價於其被半數以上的acceptor們accpet了,所以最簡單的讓chosen VALUE不可撤銷的方法是禁止acceptor們改變自己accept的VALUE,但這就違背了P1,也很可能使共識永遠無法達成,因此我們仍然希望acceptor可以更改自己accept的VALUE。然而但如果不增加額外的約束限制,那麼仍然可能有多個VALUE被chosen,這就違背了我們共識演算法等價條件的不可撤銷性,如下圖所示:

一切的罪魁禍首是proposer S5 渾然不知的提出了一個BLUE提案,而acceptor們渾然不知,只是嚴格的遵守著約束條件P1(proposer:怪我嘍?你們acceptor們怎麼不長眼吶);我們要避免這種情況,據此引入了約束條件P2:

P2. If a proposal with value v is chosen, then every higher-numbered proposal that is chosen has value v.

如果我們的共識演算法能讓acceptor們遵守P1,讓proposer們遵守P2,雖然proposer和acceptor們可能還會因為遲遲無法讓VALUE達成共識而互飛提案相互扯皮,但只要一個VALUE被chosen了,那麼後續的proposer們issue的提案中的VALUE只能是那個chosen VALUE,這樣就保證了VALUE的不可撤銷性。如果我們引入了一些其他的手段解決了相互扯皮的問題,那麼我們的演算法就是一個共識演算法了!

但是想著容易做著難,如何實現P2約束目前來說是一個很棘手的問題。此時,Lamport點明瞭幾個嘗試,試圖使用一個比P2更嚴格但更好實現的約束來解決這個問題。由於這些約束比P2更加嚴格,因此實現了這些約束,也就實現了P2。

P2a約束

P2a. If a proposal with value v is chosen, then every higher-numbered proposal accepted by any acceptor has value v.

即我們希望在acceptor處加強限制;如果半數以上的acceptor們已經accept了VALUE,那麼我們期望acceptor在accept更高proposeNum的提案時,只接受那些VALUE為v的提案;暫時不考慮具體的實現方式,不難推理得出,滿足P2a就滿足了P2。

但這又產生了一些問題:第一,一個acceptor很難知道一個VALUE是不是chosen VALUE,這在實現上有一些困難;第二,正如前文所述,只有P1和P2同時滿足時,我們的演算法才是共識演算法,但P1和P2a可能存在衝突。仍然假設我們的系統中有2個proposer,5個acceptor。proposerId為1的proposer兢兢業業的讓VALUE = RED 這個VALUE被acceptor {1,4,5}所accept,但由於網路分割槽的緣故,3沒有收到過任何提案。現在我們知道RED已經被chosen了,而渾渾噩噩的proposerId為2的proposer它 issue 了 VALUE = GREEN 的提案,這個提案被遞交到了3上。根據P1,3應當accept了GREEN這個VALUE,但根據P2a,3 accept的提案的VALUE必須是RED,這樣就產生了衝突。

最終由於各種原因,P2a約束沒有被我們所採用,Lamport又點出了第二個約束:

P2b約束

P2b. If a proposal with value v is chosen, then every higher-numbered proposal issued by any proposer has value v.

這裡我們將限制條件加在了proposer端,使得實現大為簡化,因為proposer是可以根據proposeNum以及與acceptor之間的通訊得知是否有VALUE被chosen的。雖然從構思上講,P2b的實現已經比較容易了,但我們期望讓它實現的更簡單、更嚴謹、更明確一些,Lamport繼續提出新的約束條件P2c:

P2c約束

P2c. For any v and n, if a proposal with value v and number n is issued, then there is a set S consisting of a majority of acceptors such that either (a) no acceptor in S has accepted any proposal numbered less than n, or (b) v is the value of the highest-numbered proposal among all proposals numbered less than n accepted by the acceptors in S.

在P2c約束下,proposer釋出提案<proposeNum = n, value = VALUE>之前,這個提案的proposeNum和VALUE至少要滿足如下兩個條件之中的一個:
a)acceptor中存在一個多數派S,它們均未曾accpet過 proposeNum < n 的提案,或者
b)acceptor中存在一個多數派S,它們之中至少一個acceptor曾經accept過proposeNum < n的提案,且VALUE等於S中,proposeNum < n的最大的proposeNum的提案所提出的VALUE;

P2c約束是包含了P2b的,在《Paxos Made Simple》中也有歸納證明,我個人就不敘述了(其實就是看不懂)。

但是P2c的a和b仍然難以實現。不要忘記我們的網路環境是非同步的!proposer為了推斷自己的提案是屬於情況a還是情況b,必須向acceptor傳送非同步請求獲悉acceptor當時的<lastAcceptPropNum, lastAcceptValue>。假設當proposer收到了半數以上的回覆,發現proposer的提案滿足a。很容易推斷在a下是沒有chosen的值的,因此proposer可以隨便任選一個值。但由於問詢和回覆均是非同步的,因此proposer收到回覆後,無法得知情景是不是發生了變化。假設proposer在把這個提案發布之前,一個<proposeNum = n, VALUE = BLUE> 的提案被S中的一個acceptor所接受,那麼此時p2c約束條件就被打破了。在這種情景下,如果釋出提案仍然可能會chosen出多個VALUE,破壞了VALUE的不可撤銷性。

上圖是一種在P2c約束的懸崖上走鋼絲情景。S1希望issue一個<PropNum=5, Value = RED>的提案,為此它要向半數以上的acceptor詢問他們最近一次accpet的propseNum和VALUE;當S1收到半數以上的回覆時,它感覺自己的提案符合情景a,但它不敢issue這個提案。在非同步網路環境下,由於網路延遲,proposer不敢確定在它issue提案時,acceptor們的情景仍然是它從回覆中瞭解到的那個情景。實際上在proposer準備issue時,VALUE = BLUE已經被chosen,根據P2c約束要求,S1所issue的提案中的VALUE應當是BLUE,但S1並不能從這一輪的回覆中推斷得知這個訊息。

P1a約束

basic-paxos採用了一種非常精髓的方式來避免上述情景,即將P1約束進行加強,獲得新的約束P1a:

P1a. An acceptor can accept a proposal numbered n iff it has not responded to a prepare request having a number greater than n.

P2c約束對proposer所issue的提案進行了限制,而P1a則對acceptor們accept的情景進行了限制:在proposer向acceptor詢問<lastProposeNum, lastProposeValue>時,要求這個acceptor給出承諾,不得accept任何proposeNum < n 的提案在P1a的助攻下,P2c也終於可以通過一輪簡單的問詢,確認自己的提案是不是屬於a情景或者b情景了

這樣我們得到了basic-paxos的最終約束條件:P1a再加上P2c。

二階段協議的設計

P2c + P1a 組成了basic-paxos的全部約束條件,他們也是非常詳細且易於實現的。很明顯,一個輪次的basic-paxos可以分為兩個階段:Prepare和Issue,分別對應兩個RPC(prepareRPC,acceptRPC)。現在假設這個提案的proposeNum為 n,讓我們來考察一下basic-paxos的兩個階段:

Prepare階段

根據P2c約束我們可知,proposer的提案必須要滿足a或者b;根據P1a約束我們可知,可以通過一輪對acceptor們的 <lastProposeNum,lastProposeValue> 的詢問推斷提案是否滿足a或者b;Prepare階段的目的就在於向acceptor們諮詢<lastProposeNum,lastProposeValue> 。當proposer收到了半數以上的回覆後,就可以判斷自己的提案的情況了:

0)acceptor收到了來自proposer的prepareRPC請求時,必須要向proposer承諾不再accept那些proposeNum < n的請求,為此它維持一個max_propose_num,如果 n > max_propose_num,則令自己的max_propose_num = n,並承諾拒絕一切 proposeNum < max_propose_num 的 acceptRPC。

1)如果多數派的acceptor未曾accept過 proposeNum < n 的提案,則可以得知 proposeNum < n 的提案沒有chosen出一個VALUE。此時proposer可以issue這個提案,VALUE是多少可以由proposer自行決定;或者說

2)得到的半數以上的回覆中,至少有一個acceptor過去曾經 accept 過 proposeNum < n 的提案;此時proposer必須放棄自己想要提出的VALUE,替代以收到的回覆中proposeNum最接近且小於(“舊中最新”)的那個提案中的VALUE;

Accept階段

經過Prepare階段後,proposer已經知道自己到底應該issue一個什麼VALUE了,因此它向所有的acceptor們傳送acceptRPC請求,並將自己的proposeNum(本例中是n)新增到acceptRPC中。acceptor接收到acceptRPC後,會將proposeNum與自己的max_propose_num進行比較。如果proposeNum >= max_propose_num,則設定自己的lastProposeNum = proposeNum,lastProposeValue = VALUE,必要時會更新自己的max_propose_num。

虛擬碼

Diego Ongaro在basic paxos的講解視訊中,將虛擬碼表述如下:

再談終止性(Termination)

現在回顧一下章節約束條件P2中我所說的話:

你會發現,構建一個多輪提議下仍然無法讓半數以上的acceptor們accept同一個VALUE的情景,實在是太容易太容易了。怎麼避免他們陷入這種無窮無盡的扯皮,暫時不是我們要考慮的內容,我們現在要考慮的是另一個同等重要的問題 ....

是的,讀到現在你發現,我們長篇大論了這麼久,僅僅只解決了一個問題,即如果一個VALUE已經被chosen,如何保證這個chosen VALUE不可撤銷,並設計出了滿足這一問題的實現演算法而已!proposer和acceptor可能遲遲達不成共識這一問題,仍然沒有得到較好的解決!但這一問題如果不能解決,我們的basic-paxos演算法就和Termination這一原則相牴觸了。Lamport在《Paxos Made Simple》中也注意到了這一點,他提出的解決方法是選主即盡最大努力保證系統內僅有一個proposer,以避免多個proposer間提案相互衝突的情景。但Lamport也點明,即使我們不實用選主策略,演算法仍然是可以推進的。

一點分析

直接看虛擬碼可能仍然會比較疑惑,我們考察一些具體的情景,換一種方式理解Basic-Paxos。

假定叢集中的acceptor們為{1, 2, 3, 4, 5};
1)1在prepare階段使用proposeNum = 1 聯絡了{1, 2, 3},則當前的 minProposal 為{1, 1, 1, 0, 0};然後1順利的讓自己accept了BLUE,但其他的acceptRPC全部丟失;
2) 2prepare階段使用proposeNum = 2 聯絡了{1, 2, 3},當前的 minProposal 為 {2, 2, 2, 0, 0},根據演算法,2必須要用issue的VALUE為BLUE,但它也僅讓自己accpet了BLUE,其他acceptRPC全部丟失;
3) 5在prepare階段使用proposeNum = 3 聯絡了 {3, 4, 5},則當前的 minProposal 為 {2, 2, 3, 3, 3},根據演算法,5可以使用自定義的VALUE,假定為RED;但它也僅讓自己accept了RED;
此時此刻,accpetor {1, 2, 5} 順利的accpet了VALUE,分別為 {BLUE, BLUE, NULL, NULL, RED}, accpetProposeNum分別為 {1, 2, NULL, NULL, 3};
4)通過proposeNum = 4聯絡了 {1, 2, 3},當前的minProposal 為 {4, 4, 4, 3, 3},根據演算法,3要issue的提案中的VALUE必須是BLUE。當3成功的accept了BLUE後,那麼BLUE就成功的被{1, 2, 3}所accept了,因此BLUE成功被chosen;

結論1:如果已經有VALUE被chosen了,假定acceptor們所有accpet的VALUE構成集合S,那麼proposer們所issue的任何提案的VALUE必定屬於S;

如果已經有了chosen VALUE,那麼必定有多數的acceptor已經accept了VALUE。回顧一下我們的演算法,只有當proposer發現半數以上的acceptor沒有accept過任何VALUE時,才能自己決定一個VALUE;在prepare階段,proposer與半數以上的acceptor們聯絡,必定會發現至少一個acceptor已經accept了VALUE,這些VALUE均屬於S;根據演算法,proposer必須放棄自己想要propose的VALUE;故結論1得證;

結論2:如果已經VALUE被chosen了,那麼不存在一個未chosen的VALUE,它的acceptedProposedNum比一切chosen VALUE的acceptedProposeNum更大;

在我舉的栗子中,chosen VALUE為BLUE,其最大的acceptedProposedNum為4,大於unchosen VALUE 3的acceptedProposedNum(值為3);

我們可以通過反證法來證明結論2,即假設存在一個acceptor成功accept了一個VALUE,它的acceptProposeNum比一切chosen VALUE的acceptedProposeNum更大;
根據結論0我們可知,不可能有S以外的VALUE被issue,因此這個特殊的VALUE必定是屬於S的;考察這個特殊的VALUE被accept的時機,要麼在chosen VALUE被chosen之前,要麼在之後。

情況一:如果這個SPECIAL VALUE是在chosen VALUE被chosen之前才被某個acceptor所accept的,假設對應的acceptProposeNum為specialProposeNum。由於這個SPECIAL VALUE已經被accept,它必定通過了prepare階段,那麼必定多數的acceptor的minPropose被設定為了specialProposeNum。但chosen VALUE在被chosen前的最後一步(即只需要再有一個acceptor成功accept了chosen VALUE,那麼chosen VALUE就順利被chosn了),也會和多數的acceptor取得聯絡,必定能得知specialProposeNum,因此最後的一個acceptor成功accept chosen VALUE時,對應的acceptProposeNum必定是大於specialProposeNum的,這個我們的假設相矛盾;

情況二:如果這個SPECIAL VALUE是在chosen VALUE被chosen之後才被某個acceptor所accpet的。注意此時此刻,SPECIAL VALUE尚未被任何acceptor所accept(否則,應該屬於情況一);既然SPECIAL VALUE已經被accept了,那麼它必定已經順利通過了prepare階段,這與結論1相矛盾;

因此結論2得證;

結論3:如果已經VALUE被chosen了,已經accept了chosen VALUE的acceptor不可能改變自己accept的結果;

結論3仍然可以用反證法輕鬆證明。如果某個accept了chosen VALUE的accpetor改變了自己的accept結果accept了一個unchosen VALUE,那麼必定有一個proposer成功的發出了acceptRPC,那麼這個proposer必定要通過了prepare階段,那麼根據prepare規則,這個proposer必須要發現一個acceptor的多數派M,在M中的acceptor,要麼沒有accept過任何VALUE,要麼accept的VALUE就是那個unchosen VALUE。這個多數派與accept了chosen VALUE的多數派至少有一個交集,故矛盾。

結論4:如果已經VALUE被chosen了,那麼全體acceptor達成共識的進度(即達成全部的acceptor均accept到chosen VALUE的成就)只能向前推進,不可能後退;

雖然在prepare階段,proposer可能會pick一個unchosen value,且可能無限次的pick這個unchosen value。例如說3崩潰了,5只能和{1, 2, 5}取得聯絡,對應的acceptProposeNum = {1, 2, 3},根據規則,需要重新一輪二階段演算法,提議那個acceptedProposeNum = 3的VALUE,這個VALUE是unchosen VALUE。

但只要3能夠迴歸,那麼3就有在prepare階段被找到的可能性,那麼chosen VALUE就有被issue的可能性;如果新的一輪的accept仍然沒能讓那些accept了unchosen VALUE的acceptor改變自己的結果,那麼只需重來就可以了;從具體到一般,根據結論2我們可知,只要那個持有最大的acceptProposeNum的、accept了chosen VALUE的acceptor能加入,那麼它就可能在prepare階段被聯絡到,那麼proposer就必須採用來自它的chosen VALUE;那麼就存在issue新的提案,讓那些accept了unchosen VALUE的accpetor改邪歸正的可能性;

又根據結論3,選擇了chosen VALUE的accpetor不可能改變自己的accept結果,因此只有那些accept了unchosen VALUE的acceptor可能會改變自己的VALUE。如果改變了,那麼我們向全體acceptor達成共識又邁進了非同步;如果沒能改變,只不過是重來一遍而已。

如果你學過馬爾科夫鏈,你會發現最終達成共識這個狀態就像一個吸收態

參考資料

[1] Paxos Made Simple;Lamport大仙對paxos的(一點也不)通俗解釋
[2] https://zh.wikipedia.org/wiki/Paxos演算法#演算法 ;關於paxos的wikipedia,基本上是《Paxos Made Simple》的簡中翻譯;
[3] https://zhuanlan.zhihu.com/p/220311761 ;從中理解到共識(consensus)的含義即可;
[4] https://www.youtube.com/watch?v=JEpsBg0AO6o ;Raft作者 Diego Ongaro 對 basic-paxos 和 multi-paxos 的講解,強烈推薦;
[5] https://zhuanlan.zhihu.com/p/260204158 ;可以看做是對上述視訊內容的簡中翻譯;

相關文章