分佈式協議:Paxos

提出問題

在分佈式系統中如何實現跨進程通訊,這是一個經典問題。主要的解決方案包含了兩種:web

  • 共享內存
  • 消息傳遞

本篇不記錄關於共享內存的方式,將從消息傳遞方式引出paxos算法。在基於消息傳遞通訊模型的分佈式系統中,不可避免的會發生進程變慢、被殺死或者重啓,消息可能會發生延遲、重複、丟失等問題。在一個典型的場景中,一個分佈式數據庫系統,若是各個節點初始狀態一致,每一個節點都進行相同的操做,那麼他們最後是一個一致的狀態。爲了保證每個節點都保證一致的狀態,那麼就須要在每一條指令上都執行一個共識算法以保證每個節點看到的指令(插入、更新、刪除)都是一致的。一個通用的共識算法能夠應用在許多場景中,是分佈式計算中的重要問題。所以從20世紀80年代起對於共識算法的研究就沒有中止過。算法

1982年一位叫Lamport的大神與另外兩位做者發表了一篇論文The Byzantine Generals Problem,文中已較爲晦澀的方式闡述了Paxos算法,由此打開了計算機在分佈式一致性方面的大門,這也是目前公認的解決分佈式一致性問題的有效辦法,該論文較爲難懂,如若想更好的理解還請參看Paxos Made Simple數據庫

若想了解一致性算法,那麼先要明確幾個實際需求。假設有一組能夠提出提案的進程,那麼對於一個一致性算法來講須要保證如下幾點:異步

  • 若是沒有提案被提出,那麼就不會有被選定的提案。
  • 在一次被提出的多個提案中,只有一個提案可以被選中。
  • 當一個提案被選定時,其餘進程可以獲取到該提案。

帶着這幾個需求再來深刻了解Paxos算法。分佈式

算法內容

在Paxos中共有三種角色參與,用 Proposer、Acceptor、Learner 表示(固然,容許身兼多職),Proposer負責提出提案,提案包括了提案編號和提案內容(好比:提案001-將稅率提高10%),Acceptor能夠接收提案,若提案被多數的Acceptor接受,那麼將會被提交批准(chosen),Learner只可以從Acceptor那裏學習被批准的提案。學習

上邊的說法可能很差理解,能夠將其理解爲表明與羣衆的關係,表明能夠是Proposer同時也是Acceptor,他們在會議上提出提案,由其餘的表明一同決定是否統一該提案,若是多數贊成,那麼該提案也就意味着經過,羣衆再從各個表明那裏學習提案精神。明確了角色關係,再回頭看以前的問題:優化

  • 約束1:決議(value)只有在被Proposer提出後纔可以被批准(未經批准前的決議被稱爲提案)。
  • 約束2:在一次paxos算法(可能存在多個提案)執行的過程當中,只有一個提案會被批准。
  • 約束3:只有一個提案被批准成爲決議時,Learner纔可以獲取到他。

推導過程

批准value的過程當中,首先Proposer將value發送給Acceptor,以後Acceptor對value進行接受。爲了知足只批准一個value的約束,要求經多數派接受的提案value成爲決議。這是由於不管是按照人數仍是按照權重劃分,兩組多數派至少有一個公共的Acceptor,若是每一個Acceptor只能接受一個value,約束2就能保證。.net

因而產生了一個顯而易見的新約束:code

P1:一個Acceptor必須接受(accept)第一次收到的提案。

注意 P1 是不完備的。若是剛好一半Acceptor接受的提案具備valueA,另外一半接受的提案具備valueB,那麼就沒法造成多數派,沒法批准任何一個value。進程

約束2並不要求只批准一個提案,暗示可能存在多個提案。只要提案的value是同樣的,批准多個提案不違背約束2。因而能夠產生約束 P2:

P2:一旦一個具備valuev 的提案被批准(chosen),那麼以後批准(chosen)的提案必須具備valuev。

注:經過某種方法能夠爲每一個提案分配一個編號,在提案之間創建一個全序關係,所謂「以後」都是指全部編號更大的提案。

若是 P1 和 P2 都可以保證,那麼約束2就可以保證。

批准一個value意味着多個Acceptor接受(accept)了該value。所以,能夠對 P2 進行增強:

P2a:一旦一個具備valuev 的提案被批准(chosen),那麼以後任何Acceptor再次接受(accept)的提案必須具備valuev。

因爲通訊是異步的,P2a 和 P1 會發生衝突。若是一個value被批准後,一個Proposer和一個Acceptor從休眠中甦醒,前者提出一個具備新的value的提案。根據 P1,後者應當接受,根據 P2a,則不該當接受,這種場景下 P2a 和 P1 有矛盾。因而須要換個思路,轉而對Proposer的行爲進行約束:

P2b:一旦一個具備valuev 的提案被批准(chosen),那麼之後任何Proposer提出的提案必須具備valuev。

因爲Acceptor能接受的提案都必須由Proposer提出,因此 P2b 蘊涵了 P2a,是一個更強的約束。

可是根據 P2b 難以提出實現手段。所以須要進一步增強 P2b。

假設一個編號爲 m 的valuev 已經得到批准(chosen),來看看在什麼狀況下對任何編號爲 n(n>m)的提案都含有valuev。由於 m 已經得到批准(chosen),顯然存在一個Acceptor的多數派 C,他們都接受(accept)了v。考慮到任何多數派都和 C 具備至少一個公共成員,能夠找到一個蘊涵 P2b 的約束 P2c:

P2c:若是一個編號爲n的提案具備valuev,該提案被批准(chosen),那麼存在一個多數派,要麼他們中全部人都沒有接受(accept)編號小於n的任何提案,要麼他們已經接受(accept)的全部編號小於n的提案中編號最大的那個提案具備valuev

要知足P2c的約束,Proposer提出一個提案前,首先要和足以造成多數派的Acceptor進行通訊,得到他們進行的最近一次接受(accept)的提案(prepare過程),以後根據回收的信息決定此次提案的value,造成提案開始投票。當得到多數Acceptor接受(accept)後,提案得到批准(chosen),由acceptor將這個消息告知learner。這個簡略的過程通過進一步細化後就造成了Paxos算法。

在一個paxos實例中,每一個提案須要有不一樣的編號,且編號間要存在全序關係。能夠用多種方法實現這一點,例如將序數和Proposer的名字拼接起來。如何作到這一點不在Paxos算法討論的範圍以內。

若是一個沒有chosen過任何Proposer提案的acceptor在prepare過程當中回答了一個Proposer針對提案n的問題,可是在開始對n進行投票前,又接受(accept)了編號小於n的另外一個提案(例如n-1),若是n-1和n具備不一樣的value,這個投票就會違背P2c。所以在prepare過程當中,acceptor進行的回答同時也應包含承諾:不會再接受(accept)編號小於n的提案。這是對P1的增強:

P1a:當且僅當acceptor沒有迴應過編號大於n的prepare請求時,acceptor接受(accept)編號爲n的提案。

完整算法

經過一個決議分爲兩個階段:

  • prepare階段:

    • Proposer選擇一個提案編號M並將prepare請求發送給Acceptor中的一個多數派(超過半數的子集);
    • acceptor收到prepare消息後,若是提案的編號M大於它已經回覆的全部prepare消息(回覆消息表示接受accept),則acceptor將本身上次接受的提案回覆給Proposer(ACK),並承諾再也不回覆小於M的提案;

      舉例來講,當前Acceptor已經回覆了編號爲一、二、三、四、5提案,那麼該Acceptor在接收到編號爲6的提案時,將會回覆編號5,而且承諾不會再接收編號小於6的提案。

  • 批准階段:

    • 當一個Proposer收到了半數以上Acceptor對prepare的回覆後,它將要向回覆prepare請求的Acceptor發送accept請求,包括編號n和根據P2c決定的value[M, value],若是根據P2c沒有已經接受的value,那麼value將能夠是任意值。
    • 在不違背本身向其餘Proposer的承諾的前提下,acceptor收到accept請求後即批准這個請求。

在實際運行中,每個Proposer都有可能產生多個提案,但只要系統按照當前算法運行,那麼就可以保證正確性。若是一個Proposer已經生成了更大的提案編號,那麼刪除編號較小的是一個更好的選擇,因此在Acceptor接收到一個編號爲n的請求,但其發現已經接受了比n更大的編號,那麼它會通知發送編號n提案的Proposer,讓其將這個提案刪除掉。

決議的發佈

  • 一個顯而易見的方法是當Acceptor批准一個value成爲決議時,將這個消息發送給全部learners。也就是說當提案經過時,將會有至少Acceptors個數*Learner個數的通訊次數,這對一個分佈式系統來講並非十分友好。
  • 當前咱們不考慮拜占庭將軍問題,learners能夠經過別的learners進行通訊,獲取已經經過的決議。所以Acceptor只需將批准的消息發送給指定的某一個learner(也就能夠理解爲主learner),其餘learners向它詢問已經經過的決議。這個方法下降了消息量,可是主learner存在單點問題,其下線將引發系統失效。所以Acceptor須要將accept消息發送給learners的一個子集,而後由這些learners去通知全部learners。
  • 可是因爲消息傳遞的不肯定性,可能會沒有任何learner得到了決議批准的消息。當learners須要瞭解決議經過狀況時,可讓一個Proposer從新進行一次提案。注意一個learner可能兼任Proposer。

可持續性的保證

根據上述過程當一個Proposer發現存在編號更大的提案時將終止提案。這意味着提出一個編號更大的提案會終止以前的提案過程。若是兩個Proposer在這種狀況下都轉而提出一個編號更大的提案,就可能陷入活鎖,也就是二者不停的拿出更大的提案,這違背了算法可持續性的要求。通常活鎖能夠經過隨機睡眠-重試的方法解決。

不過在這種狀況下的更好的解決方案是選舉出一個leader Proposer,僅容許leader提出提案。這樣一來只要主Proposer和過半數的Acceptor可以進行通訊,那麼提案就可以被批准。

這種狀況下的解決方案是選舉出一個leader,僅容許leader提出提案。可是因爲消息傳遞的不肯定性,可能有多個proposer自認爲本身已經成爲leader。這也就須要提供leader Proposer高可用。

總結

本文經過對分佈式系統的消息傳遞模型分析,進而引伸出Paxos算法,Paxos經過引入過半原則,和基於2PC的機制,進一步優化了2PC和3PC中包含的問題,解決的同步阻塞、腦裂、無限期等待等問題。能夠說Paxos算法是分佈式一致性共識協議中較爲理想的實現。

相關文章
相關標籤/搜索