分佈式系列文章——Paxos算法原理與推導

轉載:https://www.cnblogs.com/linbingdong/p/6253479.htmlhtml

Paxos算法在分佈式領域具備很是重要的地位。可是Paxos算法有兩個比較明顯的缺點:1.難以理解 2.工程實現更難。算法

網上有不少講解Paxos算法的文章,可是質量良莠不齊。看了不少關於Paxos的資料後發現,學習Paxos最好的資料是論文《Paxos Made Simple》,其次是中、英文版維基百科對Paxos的介紹。本文試圖帶你們一步步揭開Paxos神祕的面紗。安全

Paxos是什麼

Paxos算法是基於消息傳遞且具備高度容錯特性一致性算法,是目前公認的解決分佈式一致性問題最有效的算法之一。網絡

Google Chubby的做者Mike Burrows說過這個世界上只有一種一致性算法,那就是Paxos,其它的算法都是殘次品分佈式

雖然Mike Burrows說得有點誇張,可是至少說明了Paxos算法的地位。然而,Paxos算法也由於晦澀難懂而臭名昭著。本文的目的就是帶領你們深刻淺出理解Paxos算法,不只理解它的執行流程,還要理解算法的推導過程,做者是怎麼一步步想到最終的方案的。只有理解了推導過程,才能深入掌握該算法的精髓。並且理解推導過程對於咱們的思惟也是很是有幫助的,可能會給咱們帶來一些解決問題的思路,對咱們有所啓發。學習

問題產生的背景

在常見的分佈式系統中,總會發生諸如機器宕機網絡異常(包括消息的延遲、丟失、重複、亂序,還有網絡分區)等狀況。Paxos算法須要解決的問題就是如何在一個可能發生上述異常的分佈式系統中,快速且正確地在集羣內部對某個數據的值達成一致,而且保證不論發生以上任何異常,都不會破壞整個系統的一致性。優化

注:這裏某個數據的值並不僅是狹義上的某個數,它能夠是一條日誌,也能夠是一條命令(command)。。。根據應用場景不一樣,某個數據的值有不一樣的含義。設計

問題產生的背景

相關概念

在Paxos算法中,有三種角色:3d

  • Proposer
  • Acceptor
  • Learners

在具體的實現中,一個進程可能同時充當多種角色。好比一個進程可能既是Proposer又是Acceptor又是Learner日誌

還有一個很重要的概念叫提案(Proposal)。最終要達成一致的value就在提案裏。

注:

  • 暫且認爲『提案=value』,即提案只包含value。在咱們接下來的推導過程當中會發現若是提案只包含value,會有問題,因而咱們再對提案從新設計
  • 暫且認爲『Proposer能夠直接提出提案』。在咱們接下來的推導過程當中會發現若是Proposer直接提出提案會有問題,須要增長一個學習提案的過程。

Proposer能夠提出(propose)提案;Acceptor能夠接受(accept)提案;若是某個提案被選定(chosen),那麼該提案裏的value就被選定了。

回到剛剛說的『對某個數據的值達成一致』,指的是Proposer、Acceptor、Learner都認爲同一個value被選定(chosen)。那麼,Proposer、Acceptor、Learner分別在什麼狀況下才能認爲某個value被選定呢?

  • Proposer:只要Proposer發的提案被Acceptor接受(剛開始先認爲只須要一個Acceptor接受便可,在推導過程當中會發現須要半數以上的Acceptor贊成才行),Proposer就認爲該提案裏的value被選定了。
  • Acceptor:只要Acceptor接受了某個提案,Acceptor就職務該提案裏的value被選定了。
  • Learner:Acceptor告訴Learner哪一個value被選定,Learner就認爲那個value被選定。

相關概念

問題描述

假設有一組能夠提出(propose)value(value在提案Proposal裏)的進程集合。一個一致性算法須要保證提出的這麼多value中,只有一個value被選定(chosen)。若是沒有value被提出,就不該該有value被選定。若是一個value被選定,那麼全部進程都應該能學習(learn)到這個被選定的value。對於一致性算法,安全性(safaty)要求以下:

  • 只有被提出的value才能被選定。
  • 只有一個value被選定,而且
  • 若是某個進程認爲某個value被選定了,那麼這個value必須是真的被選定的那個。

咱們不去精確地定義其活性(liveness)要求。咱們的目標是保證最終有一個提出的value被選定。當一個value被選定後,進程最終也能學習到這個value。

Paxos的目標:保證最終有一個value會被選定,當value被選定後,進程最終也能獲取到被選定的value。

假設不一樣角色之間能夠經過發送消息來進行通訊,那麼:

  • 每一個角色以任意的速度執行,可能因出錯而中止,也可能會重啓。一個value被選定後,全部的角色可能失敗而後重啓,除非那些失敗後重啓的角色能記錄某些信息,不然等他們重啓後沒法肯定被選定的值。
  • 消息在傳遞過程當中可能出現任意時長的延遲,可能會重複,也可能丟失。可是消息不會被損壞,即消息內容不會被篡改(拜占庭將軍問題)。

推導過程

最簡單的方案——只有一個Acceptor

假設只有一個Acceptor(能夠有多個Proposer),只要Acceptor接受它收到的第一個提案,則該提案被選定,該提案裏的value就是被選定的value。這樣就保證只有一個value會被選定。

可是,若是這個惟一的Acceptor宕機了,那麼整個系統就沒法工做了!

所以,必需要有多個Acceptor

只有一個Acceptor

多個Acceptor

多個Acceptor的狀況以下圖。那麼,如何保證在多個Proposer和多個Acceptor的狀況下選定一個value呢?

多個Acceptor

下面開始尋找解決方案。

若是咱們但願即便只有一個Proposer提出了一個value,該value也最終被選定。

那麼,就獲得下面的約束:

P1:一個Acceptor必須接受它收到的第一個提案。

可是,這又會引出另外一個問題:若是每一個Proposer分別提出不一樣的value,發給不一樣的Acceptor。根據P1,Acceptor分別接受本身收到的value,就致使不一樣的value被選定。出現了不一致。以下圖:

幻燈片08.png

剛剛是由於『一個提案只要被一個Acceptor接受,則該提案的value就被選定了』才致使了出現上面不一致的問題。所以,咱們須要加一個規定:

規定:一個提案被選定須要被半數以上的Acceptor接受

這個規定又暗示了:『一個Acceptor必須可以接受不止一個提案!』否則可能致使最終沒有value被選定。好比上圖的狀況。v一、v二、v3都沒有被選定,由於它們都只被一個Acceptor的接受。

最開始講的『提案=value』已經不能知足需求了,因而從新設計提案,給每一個提案加上一個提案編號,表示提案被提出的順序。令『提案=提案編號+value』。

雖然容許多個提案被選定,但必須保證全部被選定的提案都具備相同的value值。不然又會出現不一致。

因而有了下面的約束:

P2:若是某個value爲v的提案被選定了,那麼每一個編號更高的被選定提案的value必須也是v。

一個提案只有被Acceptor接受纔可能被選定,所以咱們能夠把P2約束改寫成對Acceptor接受的提案的約束P2a。

P2a:若是某個value爲v的提案被選定了,那麼每一個編號更高的被Acceptor接受的提案的value必須也是v。

只要知足了P2a,就能知足P2。

可是,考慮以下的狀況:假設總的有5個Acceptor。Proposer2提出[M1,V1]的提案,Acceptor2~5(半數以上)均接受了該提案,因而對於Acceptor2~5和Proposer2來說,它們都認爲V1被選定。Acceptor1剛剛從宕機狀態恢復過來(以前Acceptor1沒有收到過任何提案),此時Proposer1向Acceptor1發送了[M2,V2]的提案(V2≠V1且M2>M1),對於Acceptor1來說,這是它收到的第一個提案。根據P1(一個Acceptor必須接受它收到的第一個提案。),Acceptor1必須接受該提案!同時Acceptor1認爲V2被選定。這就出現了兩個問題:

  1. Acceptor1認爲V2被選定,Acceptor2~5和Proposer2認爲V1被選定。出現了不一致。
  2. V1被選定了,可是編號更高的被Acceptor1接受的提案[M2,V2]的value爲V2,且V2≠V1。這就跟P2a(若是某個value爲v的提案被選定了,那麼每一個編號更高的被Acceptor接受的提案的value必須也是v)矛盾了。

幻燈片10.png

因此咱們要對P2a約束進行強化!

P2a是對Acceptor接受的提案約束,但其實提案是Proposer提出來的,全部咱們能夠對Proposer提出的提案進行約束。獲得P2b:

P2b:若是某個value爲v的提案被選定了,那麼以後任何Proposer提出的編號更高的提案的value必須也是v。

由P2b能夠推出P2a進而推出P2。

那麼,如何確保在某個value爲v的提案被選定後,Proposer提出的編號更高的提案的value都是v呢?

只要知足P2c便可:

P2c:對於任意的N和V,若是提案[N, V]被提出,那麼存在一個半數以上的Acceptor組成的集合S,知足如下兩個條件中的任意一個:

  • S中每一個Acceptor都沒有接受過編號小於N的提案。
  • S中Acceptor接受過的最大編號的提案的value爲V。

Proposer生成提案

爲了知足P2b,這裏有個比較重要的思想:Proposer生成提案以前,應該先去『學習』已經被選定或者可能被選定的value,而後以該value做爲本身提出的提案的value。若是沒有value被選定,Proposer才能夠本身決定value的值。這樣才能達成一致。這個學習的階段是經過一個『Prepare請求』實現的。

因而咱們獲得了以下的提案生成算法

  1. Proposer選擇一個新的提案編號N,而後向某個Acceptor集合(半數以上)發送請求,要求該集合中的每一個Acceptor作出以下響應(response)。 (a) 向Proposer承諾保證再也不接受任何編號小於N的提案

    (b) 若是Acceptor已經接受過提案,那麼就向Proposer響應已經接受過的編號小於N的最大編號的提案

    咱們將該請求稱爲編號爲NPrepare請求

  2. 若是Proposer收到了半數以上的Acceptor的響應,那麼它就能夠生成編號爲N,Value爲V的提案[N,V]。這裏的V是全部的響應中編號最大的提案的Value。若是全部的響應中都沒有提案,那 麼此時V就能夠由Proposer本身選擇
    生成提案後,Proposer將該提案發送給半數以上的Acceptor集合,並指望這些Acceptor能接受該提案。咱們稱該請求爲Accept請求。(注意:此時接受Accept請求的Acceptor集合不必定是以前響應Prepare請求的Acceptor集合)

Acceptor接受提案

Acceptor能夠忽略任何請求(包括Prepare請求和Accept請求)而不用擔憂破壞算法的安全性。所以,咱們這裏要討論的是何時Acceptor能夠響應一個請求。

咱們對Acceptor接受提案給出以下約束:

P1a:一個Acceptor只要尚未響應過任何編號大於NPrepare請求,那麼他就能夠接受這個編號爲N的提案

若是Acceptor收到一個編號爲N的Prepare請求,在此以前它已經響應過編號大於N的Prepare請求。根據P1a,該Acceptor不可能接受編號爲N的提案。所以,該Acceptor能夠忽略編號爲N的Prepare請求。固然,也能夠回覆一個error,讓Proposer儘早知道本身的提案不會被接受。

所以,一個Acceptor只需記住:1. 已接受的編號最大的提案 2. 已響應的請求的最大編號。

小優化

Paxos算法描述

通過上面的推導,咱們總結下Paxos算法的流程。

Paxos算法分爲兩個階段。具體以下:

  • 階段一:

    (a) Proposer選擇一個提案編號N,而後向半數以上的Acceptor發送編號爲N的Prepare請求

    (b) 若是一個Acceptor收到一個編號爲N的Prepare請求,且N大於該Acceptor已經響應過的全部Prepare請求的編號,那麼它就會將它已經接受過的編號最大的提案(若是有的話)做爲響應反饋給Proposer,同時該Acceptor承諾再也不接受任何編號小於N的提案

  • 階段二:

    (a) 若是Proposer收到半數以上Acceptor對其發出的編號爲N的Prepare請求的響應,那麼它就會發送一個針對[N,V]提案Accept請求半數以上的Acceptor。注意:V就是收到的響應編號最大的提案的value,若是響應中不包含任何提案,那麼V就由Proposer本身決定

    (b) 若是Acceptor收到一個針對編號爲N的提案的Accept請求,只要該Acceptor沒有對編號大於NPrepare請求作出過響應,它就接受該提案

Paxos算法流程

Learner學習被選定的value

Learner學習(獲取)被選定的value有以下三種方案:

幻燈片17.png

如何保證Paxos算法的活性

幻燈片18.png

經過選取主Proposer,就能夠保證Paxos算法的活性。至此,咱們獲得一個既能保證安全性,又能保證活性分佈式一致性算法——Paxos算法

參考資料

    • 論文《Paxos Made Simple》
    • 論文《The Part-Time Parliament》
    • 英文版維基百科的Paxos
    • 中文版維基百科的Paxos
    • 書籍《從Paxos到ZooKeeper》
相關文章
相關標籤/搜索