分佈式一致性算法-paxos介紹

概述

Paxos算法是Lamport創造基於消息傳遞的一致性算法,包括Google的Chubby在內很多系統都應用了Paxos算法,Google Chubby[1]有下面的描述:

all working protocols for asynchronous consensus we have so far encountered have Paxos at their core.

足見該算法在分佈式系統中的地位。

 

背景說明

Paxos解決的問題是,在分佈式系統中系統間如何就一個不可變變量達成一致,僅此而已!但是在該算法的基礎之上我們可以做非常有意義的事情,比如Google Chubby就是對Multi-Paxos算法的工程實現,Multi-Paxos

算法的基礎就是Paxos,通俗來說,就是多個輪次的Paxos算法的執行,確定一系列不可能變量的值,如果各個節點初始狀態一致,再執行相同的操作序列(即確定的一系列不可變變量的值),那麼最終結果必然也是一致的。這是可以應用於系統容錯和系統一致性上的。

 

條件約束

在分佈式系統一致性領域有一個號稱定理的結果 FLP Impossibility[2][3],  在異步通信環境下,沒有一個算法可以保證數據一致性。我們能夠做的就是儘可能的在Liveness和Safety之前找到一個平衡,這和CAP很相像。

Paxos做了如下保證:

Liveness:

只要存在多數派,並且多數派之間網絡是聯通的,則:

  1. 肯定會有提議被接受
  2. 被接受的提議肯定可以被其他進程學習到

 

Safety:

    Do not be eval ! 保證不做錯的事情

  1. 只有一個值被確定或者批准,不能出現第二個值把第一個值覆蓋的情況

也是兩個約束條件是Paxos的算法根基。

 

推導過程

舉個栗子: 假設,三個進程 Pi、Pj和Pk 想就變量V達成一致。

最直接的想法:

P1:  “進程集合中的任一進程向其他所有進程提議V的值,當提議被進程接收後,進程會拒絕再次對V的提議”  


這個想法是滿足Safety要求的。但是問題也很明顯,首先這個想法不允許任何進程故障,這和Liveness要求不符合!其次如果是多個進程併發對V進行賦值,無法支持。

 

優化剛剛的想法P1, 首先我們提出一個概念:法定集合(即進程集合中的多數派),將原來的想法P1升級爲

P2: “進程集合中的任一進程向其他進程提議V的值,當提議被法定集合採納,即可認爲提議已經確定;當提議被進程接收後,進程會拒絕再次對V的提議”

 

其次,對於多個進程併發對V進程賦值的場景P2 依然無法滿足,需要將 “當提議被進程接收後,進程會拒絕再次對V的提議” 這個限制去掉

 

P3: “進程集合中的任一進程向其他進程提議V的值,當提議被法定集合採納,即可認爲提議已經確定;當提議被進程接收後,進程仍然可以接收對V的提議”

如果只是允許進程可以接收多次對V的提議,系統一致性同樣也無法滿足,比如Pi.v被賦值爲a, 而且Pj.v 也採納了a,但是同時Pk.v 被賦值爲b,其實正常來說v=a 已經

形成了多數派,按照P3的約定,這個提議可以被接受了,是不能再修改的,但是由於“當提議被進程接收後,進程仍然可以接收對V的提議”,這個限制導致Safety無法滿足,

說明P3存在侷限性!

 

正常來說上面的例子中v=a已經被法定集合確認,它本身已經是確定性取值,Pk已經沒有任何選擇了,它只能提議v=a。

因此,我們調整一下策略, Pk在提議前先去向其他進程詢問一下其他人的提議值,如果所有進程v的值都爲null, 那就提議自己的v=b, 如果不爲空就提議某個值出現次數最多的

那個值,回到剛剛的例子, pk向pi和pj詢問到了v=a,此時pk也提議v=a,此時所有進程對v=a達成了一致。但是很不幸,恰巧每個值出現的次數相等怎麼辦?舉例來說就是,

Pi提議v=a, Pj提議v=b, 此時Pk向Pi和Pj詢問的結果是[v=a,1] [v=b,1] ,此時Pk懵逼了,它無法判斷該如何提議了。

看來,單純依靠v的值作爲詢問依據是不行的, 那不妨給每個v都加一個基友epoch吧, 我們姑且認爲epoch是v的一個身份,他是一個單調遞增的數字,我們將v和epoch統稱爲提案,

 

至此其實已經初步具備了Paxos的算法模型,即Paxos算法在正式提案前會有一個詢問的過程,其實也是一個兩階段的算法。同時我們也將進程內的角色細化一下,每個進程分爲兩種角色:

Proposer 和 Acceptor。 Processor 負責詢問Acceptor選擇一個合理的提案,並將該提案提交給Acceptor; Acceptor負責接收和處理Processor的詢問和提案。

 

還是剛剛的例子:如果Processor 詢問的結果爲null,則Processor 將自主的給v 賦值, 如果不爲null,則Processor需要選擇一個v作爲提案提交。 試想如果兩個Processor同時詢問且結果都爲null,則都會

向各自的法定集合提交提案,兩個法定集合必然存在至少一個common Acceptor,此時common Acceptor如何選擇這兩個Processor 的提案呢?

可以應用提案的epoch屬性來解決這個問題,我們可以設定一個規則:common Acceptor 如果同時接收到多個提案,只會接收epoch最大的提案,拒絕掉其他提案。 我們假設acceptor使用a.epoch 來表示

接收的epoch值,如果一個提案PA的epoch大於a.epoch,Acceptor則接收這個提案PA並更新自己的a.epoch=PA.epoch ,反之拒絕掉這次提案.

 

假定Pk接收到了Pi和Pj的兩個提案,且Pi.epoch > Pj.epoch, 此時依然存在兩種情形需要分別考慮:

情形一: 如果Pk先收到了Pi的提案, 當接收到Pj的提案時,直接拒絕

情形二: 如果Pk先收到了Pj的提案,此時Pk.epoch = Pj.epoch,當接收到Pi的提案時,發現Pi.epoch > pk.epoch,仍然要接收,此時顯然和Safety衝突,因爲當Pk接收到了Pj的提案時,多數派已經形成,即不可變

變量已經確定,此時不應該再接收Pi的提案!

其實我們期望的是,無論是Pk先接收到誰的提案, 結果是一致的,即:接收到epoch最大的提案,拒絕掉其他提案。

那麼我們能不能在詢問階段Pi順便告訴Pk,自己在提交階段的Epoch呢?

在詢問階段,各個Processor向Acceptor告知提交的epoch, Acceptor只接收最大的Epoch並記錄下來,這樣在提交階段,如果Processor的epoch小於自己的epoch則拒絕這次提案

回過頭繼續看剛剛的情形二, 雖然Pk先收到了Pj的提案,但是由於Pk此時的a.epoch = Pi.epoch,因爲在詢問階段Pk就已經將最大的epoch記錄,此時由於Pk的a.epoch > Pj.epoch,仍然會拒絕掉Pj


繼續看剛剛導致Pk懵逼的那個例子,Pk在提案前向Pi和Pj詢問v的取值,Pi反饋是v=a, Pj反饋是v=b, 因爲當時沒有epoch的概念,導致Pk無法選擇,此時我們已經有了epoch的支持,Pk如何選擇v的取值呢?

先說結論:選擇epoch最大的v的取值

如何證明epoch大的v就是目標值呢?[5][6]

 

綜合以上我們總結一下Paxos執行過程:

詢問階段(Propose階段)

 

Proposer 發送 Propose

       Proposer 生成全局唯一且遞增的Proposal ID,向集羣的所有機器發送 Propose,這裏無

       需攜帶提案內容,只攜帶Proposal ID即可 

Acceptor 應答 Propose
       Acceptor 收到Propose後,做出“兩個承諾,一個應答

       兩個承諾
          第一,不再應答 Proposal ID 小於等於(注意:這裏是 <= )當前請求的 Propose 第二,不再應答 Proposal ID 小於(注意:這裏是 )當前請求的 Accept請求

        一個應答
          返回已經 Accept 過的提案中 Proposal ID 最大的那個提案的Value和accepted

          Proposal ID,沒有則返回空值 

    

接收階段(Accept 階段)

Proposer 發送 Accept
   “提案生成規則”:Proposer 收集到多數派的Propose應答後,從應答中選擇存在提案

   Value的並且同時也是Proposal ID最大的提案的Value,作爲本次要發起 Accept 的提案。 如果所有應答的提案Value均爲空值,則可以自己隨意決定提案Value。然後攜帶上當前 Proposal ID,向集羣的所有機器發送 Accept請      求

應答 Accept
    Acceptor 收到 Accpet 請求後,檢查不違背自己之前作出的“兩個承諾”情況下,持久

   化當前 Proposal ID 和提案Value。最後 Proposer 收集到多數派的Accept應答後,形成 決議 

 

範例學習

case1:

服務器S1, 收到將v命名爲X的請求, v=X,被法定集合接受。 決議已經形成,決議值爲X。然後P5 學習到v=X, 並接受它。

 

case2:

v=x 只被S3 接收了,它被P4 學習到了,並被法定集合接收。

 

case3:

P 3沒有被多數派Accept(只有S1 Accept),同時也沒有被P 4學習到。由於P 4 Propose的所有應答,均未返回Value,則P 4.5可以Accept自己的Value(Y)

後續P 3Accept(X)會失敗,進行下一輪提案,最終S1,S2的Acceptor也學習到了Value(Y) 

參考

[1] The Chubby lock service for loosely-coupled distributed systems (PDF)

[2] https://en.wikipedia.org/wiki/Consensus_(computer_science)

[3] http://blog.csdn.net/chen77716/article/details/27963079

[4]https://www.zhihu.com/question/19787937/answer/82340987

[5] http://research.microsoft.com/en-us/um/people/lamport/pubs/paxos-simple.pdf

[6] https://www.zhihu.com/question/19787937 



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章