分佈式與併發【三】淺談分佈式一致性算法Paxos

一、前言

說到分佈式一致性算法,那麼必然不可避免談到Paxos,Paxos算法在分佈式領域地位非常重要,接下來簡單記錄下Paxos算法的原理。由於個人水平有限,如有錯誤還請諒解,本文參考書籍《從Paxos到ZooKeeper》與一些網絡博客。

二、背景

在分佈式系統中,經常會發生例如網絡異常、服務宕機等情況,爲了解決出現問題時數據不一致而產生了Paxos算法,可以保證無論在任何情況下都不會破壞數據的一致性。

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

三、概念介紹

提案(Proposal)

每次在對數據進行更新時,由Proposer角色進行本次操作提案的發起,由Acceptor角色進行提案的同意表決,最終完成數據的更新。

提案的內容:提案編號和提案數據值Value,姑且暫時只關注Value(修改數據的值 = Value),後面會對提案的內容進行詳細介紹。

角色

在Paxos算法中,將每個進程分配一種多種角色。分別爲:

  • Proposer
    發起提案者,當發生數據更新時,由Proposer發起(propose)提案,提案的值value就是發生改變的值。
  • Acceptor
    提案的接受者,也是投票者,如果Acceptor接受(accept)了提案,那麼該提案的值也將被選定(chosen)(超過半數以上的Acceptor接受同意提案後纔會被選定)。
  • Learners
    提案的最終結果學習者,該角色不參與提案的選舉,當提案最終被確定時同步數據,由Acceptor告知最終提案結果。

四、算法推導

從問題入手

如何保證當一個Proposer集合同時提出一個提案時,最終只有一個提案被選定,並且所有進程都能夠學習到這個提案值,如果沒有提案被提出就不會有提案被選定。

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

推導過程

一、只有一個Acceptor

如果在只有一個Acceptor角色的情況下,那麼問題非常容易解決,只要Acceptor同意收到的第一個提案,該提案就被最終選定。

如下圖所示,Acceptor只接受一個發起的提案,並最終選定提案1
只有一個Acceptor
雖然這種方式非常簡單,但是會出現單點問題,當Acceptor節點宕機後,整個系統就無法工作了。爲了避免單點問題,必須採用多Acceptor節點來進行選舉提案。

二、多個Acceptor

如下圖所示,如果多個Acceptor來接受提案,那麼最終如何選定一個唯一的提案呢?
圖2
推導:
1、首先,我們要保證,當只有一個提案提出時,也能夠被選定,那麼得出

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

2、爲了解決上圖的問題,提案1被Acceptor1接受,提案2被Acceptor2接受導致最終選定值不唯一,從而得出

一個提案被最終選定需要被半數以上的Acceptor同意選定

3、如果要滿足第二點,那麼也就意味着一個Acceptor能夠接受多個提案,否則提案將無法被選定。在最開始我們認爲一個提案的內容只包含被更新的數據value,這樣看來只有value無法滿足我們的要求。
這時,通過給提案加一個提案ID也叫提案編號來區分是哪一個提案,其中提案ID值按照提案的發起順序單調遞增,這裏不解釋具體如何生成提案ID。

提案內容由Value變更爲【提案ID,Value】

4、由於允許了多個提案被選定,那麼問題又來了,如果兩個不同值的提案被選定,豈不是無法保證數據的一致性了嗎?於是得出所有被選定的提案都有相同的value值。

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

由於提案的選定由Acceptor來進行接受選定,那麼可以將P2改爲對Acceptor的約束P2a

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

只要滿足P2a必然滿足P2, 可得出P2a -> P2

5、此時感覺一切都很好,但是由於分佈式系統中,網絡波動和機器的宕機情況的發生,可能會出現數據不一致的情況。如下圖所示:
在這裏插入圖片描述
其中Proposer1發生了網絡異常或者宕機,此時Proposer2進行了提案【M1,V1】,此時系統中的超過半數的Acceptor選定了該提案,
然後Proposer1恢復了,將提案發給了Acceptor1,由於Acceptor沒有接受過任何提案,根據之前 P1:一個Acceptor必須接受它收到的第一個提案,此時V2被選定,出現了數據不一致。並且違反了我們P2a的規定,此時V2 != V1。

6、爲解決上面出現的問題,從而對P2a約束進一步進行強化,P2a是對Acceptor的約束,而P2b是對Proposer的約束

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

7、最終證明

P2c:如果一個提案【Mn,Vn】被提出,那麼肯定存在一個超過半數已上的Acceptor集合S,滿足以下兩個條件中的任何一個。

  • S中不存在任何批准過編號小於Mn的提案Acceptor。
  • 選取S中所有Acceptor批准的編號小於Mn的提案,其中最大的那個提案Value值一定是Vn。

至此,只需要保證P2c,我們就可以保證P2b,從而保證P2和P1,最終保證數據的一致性。

三、Proposer生成提案

通過上面的推理,我們確定了,當一個新的提案被提出時,提案的value一定爲當前被選定提案的Value值。那麼Proposer如何能夠滿足這一點呢?

提案生成算法

步驟一(Prepare):
Proposer生成一個新的提案編號M,然後向半數已上的Acceptor集合發送請求,要求每個Acceptor做出ACK響應。

  • Acceptor保證不會接受比編號M小的提案
  • Acceptor如果已經接受過提案,那麼將當前收到過提案中編號最大的那一個返回。

步驟二(Accept):
在收到所有Acceptor的響應後,生成一個提案【M,V】,其中V值由響應結果決定,如果沒有Acceptor接受過提案,那麼V值自己決定,如果有Acceptor接受過提案,則更新爲所有返回提案中編號最大的V。

四、Acceptor接受提案

Acceptor可以忽略任何請求,包括prepare和accept。
Acceptor接受一個提案的條件爲:

P1a:一個Acceptor只要尚未響應過任何編號大於M的Prepare請求,那麼他就可以接受這個編號爲M的提案。

最終可以得出,Acceptor只需要關心目前接受的最大編號的提案,小於該編號的不予以響應,另外關心當前已經響應的提案編號,保證不接受任何小於已經響應提案編號的提案。

五、Learners學習提案

當最終提案被確定後,提案的值要被所有的Learners學習同步數據。
那麼如何進行數據同步呢?

方案一
當Acceptor確定一個提案後,發送消息給所有的Learners,這樣Learners可以第一時間獲取到最新的值,但是需要所有Acceptor和Learners進行通信,通信次數爲二者個數的乘積。

方案二
當Acceptor確定一個提案後,發送消息給Learners集合中的主Leader,然後由Leader和所有Learners通信,這樣可減少通信次數,但是會出現Leader單點問題。

方案三
當Acceptor確定一個提案後,發送消息給Learners集合中的一個子集,然後由子集進行通信,這樣是方案一與方案二的折中,既不會出現單點問題,也不會出現通信次數過多,子集的個數越多可靠性越好。但是網絡通信複雜度較高

小結

至此,推導過程已經全部完成。我們來簡單回顧一下:
1、一個提案的內容,需要包含提案編號M和值V
2、一個提案被選定需要超過半數的Acceptor集合接受
3、Proposer生成提案前先確定當前已經被接受的最大提案值
4、Acceptor不接受小於當前已經接受或響應的提案編號的提案
5、Learners數據同步

五、Paxos算法描述

通過上面的推導,應該已經瞭解了Paxos的執行過程。
接下來對Paxos算法流程簡單描述一下,整個算法分爲兩個階段

階段一:Prepare

1、首先Proposer生成唯一的遞增提案編號M,然後向半數已上的Acceptor發送Prepare請求,等待響應。

2、此時Acceptor判斷,如果已經想響應過比M大的提案,則對當前提案不予以響應,否則返回已經響應過的提案中編號最大的那個,同時該Acceptor承諾不再接受任何編號小於M的提案。

3、如果Acceptor未超過半數響應,則重新發起提案,如果Acceptor響應內容含有提案,則選定所有返回提案中編號最大的那個,如果沒有響應提案,則自己決定提案值V。

階段二:Accept

1、向半數已上的Acceptor發送Accept請求,期望Acceptor接受提案。

2、如果Acceptor沒有對編號大於M的其他提案做出響應,則接受該提案。

階段三:Sync

如果一個提案超過半數已上的Acceptor接受,那麼該提案被最終選定,並且Acceptor向Learners發送選定提案的消息,Learners進行數據同步。

Paxos算法時序圖

在這裏插入圖片描述
至此,Paxos算法已經全部講完了,其中可能有人發現了一個問題,那就是該算法活性無法保證,如果有兩個提案依次進行提案請求,那麼該算法將陷入死循環,無法最終選定一個Value,如圖所示:
在這裏插入圖片描述
兩個proposer依次提交prepare請求,都會執行響應成功,但是在提交accept請求時,都會被忽略,這樣就會造成死循環,無法保證算法活性。

所以爲避免這種問題,採用選舉一個Leader Proposer來進行提案的提交,這樣總有一個Proposer提交提案,就不會出現問題,當Leader Proposer掛掉時,再進行重新選舉推選一個新的Leader避免單點問題。

六、總結

本文已經詳細的總結了Paxos算法的推導過程和運行的時序調用流程。可能還有很多細節沒有說清楚,請原諒水平有限。

本文中的圖爲個人所畫,如果有引用,請麻煩標註參考本文地址。

參考資料
【1】《Paxos Made Simple》
【2】《從Paxos到ZooKeeper》
網絡中部分博客。

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