Paxos Made Simple 論文解讀

最近在看zookeeper相關學習資料,瞭解到了拜占庭將軍問題和Paxos算法都是Leslie Lamport 老爺子提出來的,這兩個問題都構造了一個現實場景,讓人印象深刻。

接下來開始解讀Paxos一致性協議算法。
論文原文

Section 1 一致性問題

對於一致性算法的要求如下:

  • 只有被提議的值纔有可能被選中
  • 只有一個值能被選中
  • 直到提議的值被選中後,進程才能獲悉到被選中的值

關於活性liveness,這裏簡單說明了一下:保證一些提議的值最終會被選中,並且這個選中的結果能夠被其他進程所獲悉。

在該算法中,有三種角色:proposers, acceptors, 和 learners. 在實現中,一個進程可能對應多個角色。

在進程通信中,這裏做了兩點假設:1. 異步 2. 非拜占庭

  • 每個角色的處理速度不一致,有可能會失敗或者重啓。由於有可能會在提議值被選中後出現失敗的情況,故有一些必要的信息需要進行保存,使得重啓後能夠恢復。
  • 消息在通信過程中花的時間不一樣,有可能會丟失或者重複發送,但是不會被污染(拜占庭將軍問題中的背叛情況)

Section 2 選擇一個值

最簡單的實現方式就是選擇一個接收者acceptor,其他提議者proposer發送提議proposal, acceptor選擇最先接收到的proposal作爲選定的結果。

當然,這種簡單的方式存在單點問題,一旦接收者出現失效就無法繼續。

所以,我們試着使用另外一種方式:使用多個接收者acceptors,提議者proposer發送提議值proposed value 到一個接收者集合 set of acceptors。 一旦有足夠多的接收者批准了提議值,那麼該提議值被選中。這裏提到一個概念足夠多,原文解釋了一下,意思就是要保證兩個足夠多的接收者集合必然存在交集,這也是後面要使用的條件之一。

要求最後能夠有提議值被選中,即使只有一個提議值被一個提議者提出。這就意味着:

P1: 一個接收者必須批准它所收到的第一個提議值

這裏原文提出了一個問題,在差不多同一時刻可能會有多個提議值被提出,這時會導致接收者批准了不同的提議值,而最終沒有一個提議值能佔大多數。即使只有兩個提議值被提出,也會出現各自佔一半的情況(基數個接收者會因爲有一個失效而出現投票平局的情況)

鑑於上述情況,爲了即滿足P1又滿足提議值被大多數接收者所批准,那麼就必須讓接收者可以批准多個提議值。爲了跟蹤每一個提議被批准的情況,我們通過給每一個提議定義一個唯一的全局有序ID,那麼每個提議都可以表示爲(ID, Value)對。系統最終要達到的一致性指的是對value達成一致,故我們允許有多個提議Proposal被選中,但是這些提議必須擁有同樣的提議值(ID可以不同,代表不同提議者或者不同時間提出來的)。這時我們可以直觀的得到一個保證一致性的要求:

P2: 如果一個提議值爲v的提議被選中,那麼任何更大ID號且被選中的提議也必須擁有同樣的提議值v

由於提議是全局有序的,要求P2就保證了所有被選中的提議擁有同樣的提議值。

又因爲選中的提議是從批准的提議裏面選擇的,故P2的一個更強版本如下:

P2a:如果一個提議值爲v的提議被選中,那麼任何更大ID號且被批准的提議也必須擁有同樣的提議值v

我們依然保持着P1來保證最終有提議被選中。由於通信過程的異步情況,即使一個提議值爲v的提議已經被選中了,可能還存在一個接收者c一直沒有批准任何提議。如果此時它接收到了一個更大ID號的提議,且該提議與選中的提議擁有不同提議值。由於條件P1,此時接收者c需要批准該提議,但是這樣就會違背P2a。爲了保證P1和P2a兼容,我們需要滿足一個更強的版本P2b

P2b:如果一個提議值爲v的提議被選中,那麼ID號更大的提議在提出時擁有同樣的提議值v

很顯然,這個更強版本的P2b滿足的話,P2a和P2自然就滿足了。

接下來我們來看怎麼來保證P2b。我們假定(m, v)的提議被選定,那麼需要證明提議序號爲n > m擁有提議值v。爲了讓證明簡單一點,我們使用數學歸納法。假定序號m…(n-1)的提議都擁有提議值v。由於m提議被選中,故必然存在一個大多數接收者集合C,C裏面的接收者至少批准m…(n-1)中的提議。接下來我們來構造提議:

P2c:對於任何一個提議(n, v),都必然存在這麼一個大多數接收者集合S,保證以下任意一個條件成立。(a) S內沒有任何一個接收者批准過序號比該提議小的提議 (b) v等於S內所有接收者的所有被批准的提議中序號最大且小於n的提議的提議值。

通過這種構造方式,只要我們能夠保證P2c,那麼P2b的條件就滿足了。

爲了保證P2c,我們在構造提議的時候,需要獲取序號小於該提議的已被批准或者將被批准的提議序號以及對應的提議值(由於存在異步情況,有些序號較小的提議可能在我們構造新提議時還未被批准)。獲取已批准的提議很簡單,但是要預測哪些提議將被批准很困難,所以在這裏構造了一個巧妙的方式:在構造提議的時候要求接收者作出一個承諾,不再批准序號小於n的提議。所以整個提議的流程如下:

  1. 選擇一個新的提議序號n,發送給一個足夠大的接收者集,要求以下答覆:

    1. 承諾不再批准序號小於n的提議
    2. 如果批准過序號小於n的提議,返回已批准過的提議中最大序號的提議
  2. 如果在階段1中,獲取到了大多數的相應,那麼就可以生成該序號的提議,且提議值爲返回響應中序號最大的提議的提議值,如果所有的響應都沒有批准過小於n的提議,那麼就可以任意選擇提議值v

1階段稱爲準備階段,發出的請求稱爲準備請求;創建完提議後,該提議者把提議發給接收者集,希望接收者批准該提議, 稱爲批准請求。

上述是從提議者的角度描述該提議構造算法,那麼對於接收者呢?它會收到兩種不同的請求:1. 準備請求 2. 批准請求。 接收者可以選擇忽略請求而不影響一致性。只有在允許的情況下,它會迴應準備請求和批准請求(在沒有承諾不允許的情況下)。亦即以下

P1a: 如果對大於數值n的準備請求沒有被承諾過,那麼一個接收者可以批准序號爲n的提議。

很明顯P1a已經包含了P1。

至此我們已經完成了一個一致性協議算法,保證最終得到一個唯一的提議值。最終的算法只要加上下面的一些小優化即可。

一個已經對大於序號n的提議作出承諾的接收者,如果它收到序號爲n的提議的準備請求時,選擇忽略它。同樣如果接收者已經批准過一個提議,那麼針對該提議的準備請求時也選擇忽略它(可能由於網絡問題等原因而導致消息重發)

基於以上的優化,接收者只要記錄當前批准過的最大序號的提議以及提議值,還有它所承諾過的最大序號的提議即可。由於P2c的要求,接收者需要記錄它的信息以防止由於失敗而需要重啓導致忘記它做過的承諾和批准過的提議信息。

那麼我們重新來整理一下該一致性協議算法:

  1. 階段1

    1. 提議者選擇一個序號n,並把該序號用準備請求的方式發給大多數的接收者
    2. 如果接收者接收到該序號的準備請求,並且該序號比它之前承諾過的序號都大,那麼該接收者對該序號進行響應;如果該接收者之前批准過序號小於n的提議,那麼就返回批准過的最大序號提議(包含序號和提議值)。
  2. 階段2

    1. 如果前述提議者收到大多數的接收者的準備請求響應,那麼它就產生一個提議(n, v)並把該提議發給接收者請求批准。對於v的選取,如果階段1返回的響應中包含提議,那麼選取序號最大的提議的提議值作爲v,如果響應中沒有包含提議,那麼該v值可以隨意選取。
    2. 如果一個接收者接收到一個序號爲n的提議的批准請求,只要它未對序號大於n的提議進行過承諾,那麼它就批准該請求。

總結完該算法後,原文還提了一下性能的優化:提議可以隨時被拋棄而不影響準確性,如果已經有別的提議者開始生產更大序號的提議時,這時小序號的提議者可以考慮放棄原有的小序號提議。所以一個接收者如果收到一個較小的序號提議時應該告知提議者,已經有更大序號的提議被承諾過了。這樣,原先小序號提議者就可以快速放棄進而產生大序號的提議。

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