Paxos化繁爲簡

摘要

當以淺顯的英語呈現,Paxos算法非常簡單。

1. 引言

用於實現容錯分佈式系統的Paxos算法被認爲難以理解,可能是因爲對於許多讀者來說對其初始介紹是希臘語[5]。實際上,它是最簡單和最明顯的分佈式算法之一。其核心是一致性算法 - [5]中的“synod”算法。下一節表明,這種一致性算法幾乎不可避免地來自我們希望它滿足的特性。最後一節解釋了完整的Paxos算法,該算法是通過直接應用一致性建立分佈式系統的狀態機方法獲得的 - 這種方法應該是衆所周知的,因爲它是關於分佈式系統理論文章[4]中最常被引用的主題。

2. 一致性算法

2.1 存在的問題

假設有一個可以提出值的進程集合。一致性算法確保只選擇所提出的值中的某單一值。如果沒有提出任何值,則不應選擇任何值。如果已選擇某個值,則進程應該能夠學習所選值。達成一致性的安全要求是:

  • 只能選擇已提出的值,
  • 只選擇一個值,並且
  • 除非實際已經選擇,否則進程永遠不會知道已選擇了某個值。

我們不會嘗試指定明確的活躍度要求。但是,目標是確保最終選擇某一提出值,如果選擇了一個值,那麼一個進程最終可以獲得該值。

我們讓一致性算法中的三個角色由三類代理履行:提議者、接受者和學習者。在一個實現中,單個進程可以充當多個代理,但是代理到進程的映射在這裏並不引起我們關注。

假設代理可以通過發送消息來相互通信。我們使用慣常的異步非拜占庭模型,其中:

  • 代理以任意速度運行,可能因停止而失敗,並可能重新啓動。由於所有代理在選擇值後可能會失敗,然後重新啓動,因此除非某些信息可以由失敗並重新啓動的代理記住,否則無法解決問題。
  • 消息可能需要很長時間才能傳遞,可能會重複,也可能會丟失,但它們不會被破壞。

2.2 選擇一個值

選擇值的最簡單方法是使用單個接受者代理。提議者向接受者發送提議,接受者選擇其收到的第一個提議值。雖然簡單,但這種解決方案並不令人滿意,因爲接受者的失敗使得任何進一步的進展都變得不可能。

那麼,讓我們嘗試另一種選擇值的方法。我們使用多個接受者代理而不是單個接受者。提議者將提議值發送給一組接受者。接受者可以接受提議的值。當足夠多的接受者接受它時,則選擇該值。多少是足夠多?爲了確保只選擇一個值,我們可以讓一個足夠大的集合由任意大多數代理組成。因爲任何兩個大多數都有至少一個共同的接受者,如果接受者最多可以接受一個值,則這是有效的。(在許多論文中都觀察到了大多數的明顯概括,顯然從[3]開始。)

在沒有失敗或消息丟失的情況下,即使只有單個提議者提議的一個值,我們也希望選擇一個值。這意味着要求:

P1. 接受者必須接受其收到的第一個提議。

但是這個要求引發了一個問題。不同的提議者可以在幾乎同時提出多個值,導致每個接受者都接受了一個值的情形,但沒有單個值被大多數接受者接受。即使僅提出兩個提議值,如果每個值被約一半的接收者接受,那麼單個接受者的失敗可能使得無法瞭解已經選擇了哪個值。

P1和僅在大多數接受者接受時選擇值的要求意味着必須允許接受者接受多個提議。我們通過爲每個提議分配一個(自然)編號來跟蹤接受者可能接受的不同提議,因此提議包含提議編號和值。爲防止混淆,我們要求不同的提議具有不同的編號。如何做到這取決於實現,所以現在我們只是假定它。當具有該值的單個提議已被大多數接受者接受時,將選擇這個值。在這種情況下,我們說已經選擇了提議(及其值)。

我們可以允許選擇多個提議,但我們必須保證所有選擇的提議具有相同的值。通過對提議編號的歸納,足以保證:

P2. 如果選擇了具有值v的提議,則所選擇的每個更高編號的提議都具有值v

由於編號是完全有序的,因此條件P2保證了僅選擇單個值的關鍵安全特性。

要被選擇,必須至少有一個接受者接受提議。因此,我們可以通過滿足以下條件來滿足P2:

P2a. 如果選擇了具有值v的提議,則任何接受者接受的每個更高編號的提議都具有值v。

我們仍然保留P1以確保選擇某一提議。由於通信是異步的,因此可以選擇一個提議,即使其中某個特定的接受者c從未接收過任何提議。假設一個新的提議者“醒來”併發出一個具有不同值的更高編號的提議。P1要求c接受此提議,這違反了P2a。同時保持P1和P2a需要加強P2a:

P2b. 如果選擇了具有值v的提議,則由任何提議者發佈的每個更高編號的提議具有值v。

由於提議必須在提議可以被接受者接受之前由提議者發佈,P2b意味着P2a,而P2a又意味着P2。

爲了發現如何滿足P2b,讓我們考慮如何證明它是成立的。我們假設選擇了一個編號爲m和值爲v的提議,並表明任何以編號n>m發出的提議也具有值v。我們可以通過在n上使用歸納來使證明更容易,因此我們可以證明提議編號n具有值v的附加假設是每個以m…(n-1)中編號發出的提議具有值v,其中i…j表示從i到j的數字集合。對於選擇的編號爲m的提議,必須有一些由大多數接受者組成的集合C,使得C中的每個接受者都接受它。將此與歸納假設相結合,選擇m的假設意味着:

C中的每個接受者都接受了一個編號在m..(n-1)範圍中的提議,並且任何接受者接受的編號爲m..(n-1)的每個提議都具有值v。

由於任何由大多數接受者組成的集合S至少包含C中的一個成員,因此我們可以得出結論,通過確保維護以下不變量,編號爲n的提議具有值v:

P2C. 對於任何v和n,如果發佈了具有值v及編號n的提議,則存在由大多數接受者組成的集合S,使得(a) S中的接受者沒有接受任何編號小於n的提議,或者(b) v是所有提議(S中的接受者所接受的提議的編號小於n)中編號最高的提議的值。

因此,我們可以通過保持P2c的不變性來滿足P2b。

爲了保持P2c的不變性,想要發佈編號爲n的提議的提議者必須獲得編號最大的提議(其編號小於n,如果有的話,已經或將被某些大多數接受者中的每個接受者接受)。瞭解已經接受的提議很容易;預測未來的提議接受很難。提議者不是試圖預測未來,而是通過提取不會有任何此類提議接受的承諾來控制它。換句話說,提議者要求接受者不再接受任何編號小於n的提議。這導致以下用於發佈提議的算法。

  1. 提議者選擇新的提議編號n並向某些接受者集合中的每個成員發送請求,要求其回覆:

    • 承諾永遠不再接受編號小於n的提議,和
    • 有最高編號的提議(小於其已接受的n,如果有的話)。

    我將這樣的請求稱爲編號爲n的準備請求。

  2. 如果提議者從大多數接受者收到請求的響應,則它可以發出具有編號n和值v的提議,其中v是響應中編號最高的提議的值,或者是提議者選擇的任何值,如果響應者報告沒有提議。

提議者通過向一組接受者發送提議已被接受的請求來發布提議。(這不一定是響應初始請求的同一組接受者。)讓我們稱之爲接受請求。

這描述了提議者的算法。接受者是怎麼樣的?它可以從提議者那裏收到兩種請求:準備請求和接受請求。一個接受者可以忽略任何請求而不影響安全性。因此,我們只需要說明何時允許響應請求。它始終可以響應準備請求。它可以響應接受請求,接受提議,如果它沒有承諾不接受。 換一種說法:

P1a. 如果接受者未響應具有大於編號n的準備請求,則接受者可以接受編號爲n的提議。

觀察到P1a包含P1。

我們現在有一個完整的算法來選擇滿足所需安全特性的值 - 假設提議編號唯一。通過進行一次小的優化來獲得最終算法。

假設接受者收到編號爲n的準備請求,但它已經響應了編號大於n的準備請求,從而承諾不接受任何編號爲n的新提議。然後,接受者沒有理由響應新的準備請求,因爲它不接受提議者想要發佈的編號爲n的提議。所以我們讓接受者忽略這樣的準備請求。我們也忽略了對已經接受的提議的準備請求。

通過此優化,接受者只需要記住它已接受的最高編號提議以及它已響應的編號最高的準備請求的編號。由於P2c無論故障與否必須保持不變,接收者必須記住此信息,即使它失敗然後重新啓動。請注意,提議者可以隨時放棄提議並忘記所有提議 - 只要它永遠不會嘗試使用相同的號碼發佈另一個提議。

將提議者和接受者的行爲放在一起,我們看到算法在以下兩個階段中運行。

  1. 階段1

    • 提議者選擇編號n的提議並向大多數接受者發送編號爲n的準備請求。
    • 如果接受者收到的編號n的請求的編號大於其已經回覆的任何準備請求的編號,則它會響應該請求,並承諾不再接受編號小於n的提議並保存已接受的編號最高的提議(如果有)。
  2. 階段2

    • 如果提議者從大多數接受者收到對其準備請求(編號爲n)的響應,則它向每個接受者發送一個接受請求,以獲得編號爲n且值爲v的提議,其中v是響應中提議編號最高的值,如果響應未報告任何提議,則爲任何值。
    • 如果接受者收到對編號爲n的提議的接受請求,則它接受該提議,除非它已經響應了具有大於編號n的準備請求。

提議者可以製作多個提議,只要它遵循每個提議的算法即可。它可以隨時放棄處於協議過程中的提議。(即使在提議被放棄很久之後,提議的請求和/或響應可能纔到達目的地,也可以保持正確性。)如果某些提議者已經開始嘗試發佈更高編號的提議,那麼放棄提議可能是一個好主意。因此,如果接受者因爲已經收到了更高編號的準備請求而忽略了準備或接受請求,那麼它應該通知提議者,然後提議者應該放棄其提議。這是一種性能優化,不會影響正確性。

2.3 獲取選擇的值

要了解已選擇了一個值,學習者必鬚髮現提議已被大多數接受者接受。顯而易見的算法是讓每個接受者在接受提議時,給所有學習者響應,向他們發送該提議。這允許學習者儘快找出所選擇的值,但是它要求每個接受者對每個學習者做出響應 - 響應數量等於接受者數量和學習者數量的乘積。

非拜占庭式失敗的假設使得一個學習者很容易從另一個學習者那裏發現一個值被接受了。我們可以讓接受者以他們對一個傑出學習者的接受來響應,這反過來又會在選擇了一個值時通知其他學習者。這種方法需要額外的輪次讓所有學習者發現所選擇的值。它也不太可靠,因爲傑出的學習者可能會失敗。但它需要響應的數量僅等於接受者數量和學習者數量的總和。

更一般地,接受者可以用他們對一組傑出學習者的接受來響應,每個傑出學習者可以在選擇了一個值時通知所有學習者。使用更多的傑出學習者以更高的通信複雜性爲代價提供更高的可靠性。

由於消息丟失,可以選擇一個沒有學習者發現的值。學習者可以向接受者詢問他們接受了哪些提議,但是接受者的失敗可能使得無法知道是否多數接受者已接受了某個特定的提議。在這種情況下,學習者只有在選擇新提議時纔會知道選擇了什麼值。如果學習者需要知道是否已經選擇了某個值,則可以使用上述算法讓提議者發出提議。

2.4 推進

很容易構建一個場景,其中兩個提議者各自繼續發佈一系列具有越來越大編號的提議,其中沒有一個被選中。提議者p完成提議編號n1的階段1。 然後,另一個提議者q完成提議編號n2>n1的階段1。提議者p的階段2對編號爲n1的提議的接受請求被忽略,因爲接受者都承諾不接受任何編號小於n2的新提議。因此,提議者p然後開始並完成新的編號n3>n2的提議的階段1,從而導致提議者q的第二個階段2的接受請求被忽略。如此往復。

爲了保證向前推進,必須選擇一位傑出的提議者作爲嘗試發佈提議的唯一提議者。如果傑出的提議者可以與大多數接受者成功通信,並且如果它使用的編號大於任何已使用的提議,那麼它將成功發佈被接受的提議。如果它獲知具有更高提議編號的某些請求,通過放棄提議並再次嘗試,則傑出提議者最終將選擇足夠高的提議編號。

如果系統(提議者,接受者和通信網絡)足以正常工作,則可以通過選擇一個傑出的提議者來實現活躍性。Fischer,Lynch和Patterson[1]的著名結果意味着選擇提議者的可靠算法必須使用隨機性或實時性 - 例如,通過使用超時。但是,無論選舉的成敗與否,都能確保安全。

2.5 實現

Paxos算法[5]假設一個進程網絡。在其一致性算法中,每個進程都扮演着提議者、接受者和學習者的角色。該算法選擇一個領導者,該領導者扮演傑出提議者和傑出學習者的角色。Paxos一致性算法正是上面描述的算法,其中請求和響應作爲普通消息發送。(響應消息標記有相應的提議編號以防止混淆。)在故障期間保留的穩定存儲用於維護接受者必須記住的信息。在實際發送響應之前,接受者將其預期響應記錄在穩定存儲中。

剩下的就是描述保證不會發出兩個具有相同編號的提議的機制。不同的提議者從不相交的數字集中選擇他們的編號,因此兩個不同的提議者從不發出具有相同數字的提議。每個提議者都會記住(在穩定存儲中)它嘗試發佈的編號最高的提議,並以高於其已使用的提議編號開始階段1。

3. 實現狀態機

實現分佈式系統的一種簡單方法是作爲將命令發佈到一箇中央服務器的客戶端集合。可以將服務器描述爲以某種順序執行客戶端命令的確定性狀態機。狀態機具有當前狀態;它通過將命令作爲輸入併產生輸出和新狀態來執行步驟。例如,分佈式銀行系統的客戶端可能是櫃員,狀態機狀態可能包括所有用戶的帳戶餘額。當且僅當餘額大於提取的金額時,執行退出將通過執行降低帳戶餘額的狀態機命令來執行提取,從而產生新舊餘額作爲輸出。

如果該服務器發生故障,則使用單箇中央服務器的實現將失敗。因此,我們使用一組服務器,每個服務器獨立地實現狀態機。因爲狀態機是確定性的,所以如果它們都執行相同的命令序列,則所有服務器將產生相同的狀態序列和輸出。然後,發出命令的客戶端可以使用任何服務器爲其生成輸出。

爲了保證所有服務器執行相同的狀態機命令序列,我們實現了Paxos一致性算法的一系列單獨實例,第i個實例選擇的值是序列中的第i個狀態機命令。每個服務器在算法的每個實例中扮演所有角色(提議者,接受者和學習者)。目前,我假設服務器集是固定的,因此一致性算法的所有實例都使用相同的代理集。

在正常操作中,單個服務器被選爲領導者,其在一致性算法的所有實例中充當傑出提議者(唯一嘗試發佈提議的提議者)。客戶端向領導者發送命令,領導者決定每個命令應該出現在序列中的哪個位置。如果領導者決定某個客戶端命令應該是第135個命令,則它嘗試將該命令選擇爲一致性算法的第135個實例的值。它通常會成功。它可能因爲失敗而失敗,或者因爲另一臺服務器也認爲自己是領導者並對第135個命令應該是什麼有不同的想法。但是,一致性算法確保最多隻可以選擇一個命令作爲第135個命令。

這個方法有效的關鍵在於,在Paxos一致性算法中,直到階段2纔會選擇要提議的值。回想一下,在完成提議者算法的階段1之後,要麼確定要提議的值,要麼提議者可以自由提議任何值。

我現在將描述Paxos狀態機實現在正常操作期間的工作原理。稍後,我將討論可能出現的問題。我考慮當前任領導者剛剛失敗並且選擇了新的領導者時會發生什麼。(系統啓動是一種特殊情況,尚未提出任何命令。)

作爲一致性算法的所有實例中的學習者,新領導者應該知道已經選擇的大多數命令。假設它知道命令1-134,138和139,即在一致性算法的實例1-134,138和139中選擇的值。(稍後我們將看到如何在命令序列中出現這樣的差距。)然後它執行實例135-137的階段1和所有大於139的實例。(我在下面描述如何完成這個。)假設這些執行的結果確定了實例135和140中要提出的值,但在所有其他實例中保留了提議值。然後領導者爲實例135和140執行階段2,從而選擇命令135和140。

領導者以及學習領導者知道的所有命令的任何其他服務器現在可以執行命令1-135。然而,它也不能執行它也知道的命令138-140,因爲還沒有選擇命令136和137。領導者可以將客戶請求的下兩個命令作爲命令136和137。相反,我們通過提出一個使狀態保持不變的特殊“noop”命令作爲命令136和137來立即填補空隙。(它通過執行一致性算法的實例136和137的階段2來實現。)一旦選擇了這些無操作命令,就可以執行命令138-140。

現在已經選擇了命令1-140。對於一致性算法中所有超過140的實例,領導者也完成了階段1,並且可以在這些實例的階段2中自由地提出任何值。它將命令編號141分配給客戶端請求的下一個命令,將其提議作爲一致性算法的實例141的階段2中的值。它建議它接收的下一個客戶端命令作爲命令142,依此類推。

領導者在得知其所提出的命令141已被選擇之前可以提出命令142。它在提議命令141中發送的所有消息都可能丟失,並且在任何其他服務器已經知道領導者提出的命令141之前選擇命令142。當領導者未能收到對實例141中階段2消息的預期響應時,它將重新傳輸這些消息。如果一切順利,將選擇其建議的命令。但是,它可能首先失敗,在所選命令的序列中留下間隙。通常,假設領導者可以提前獲得a命令 - 也就是說,在選擇命令1到i之後,它可以提出命令i+1到i+a。然後可能出現高達a-1個命令的間隙。

對於實例135-137和所有大於139的實例,新選擇的領導者爲無限多個一致性算法實例(在上述場景下)執行階段1。對於所有實例使用相同的提議編號,它可以通過對其他服務器發送單個合理短消息來執行此操作。在階段1中,只有當接收者已經收到來自某個提議者的階段2消息時,它纔會響應不止一個簡單的OK。(在該場景中,僅針對實例135和140的情況。)因此,服務器(充當接受者)可以使用單個合理短消息來響應所有實例。因此,執行這些無限多個階段1的實例不會產生任何問題。

由於領導者的失敗以及新領導的選舉應該是罕見的事件,執行狀態機命令的有效性成本 - 即在命令/值上達成一致性 - 是僅執行一致性算法的階段2的成本。可以證明,Paxos一致性算法的階段2具有在存在故障時達成一致的任何算法的最小可能成本[2]。因此,Paxos算法基本上是最優的。

關於系統正常運行的討論假定總是有一個單一領導者,除了當前領導者的失敗和新領導者選舉之間的短暫時期。在異常情況下,領導者選舉可能會失敗。如果沒有服務器充當領導者,則不會提出新的命令。如果多個服務器認爲它們是領導者,那麼他們都可以在一致性算法的相同實例中提出值,這可以防止選擇任何值。但是,安全性得以保留 - 兩個不同的服務器永遠不會對作爲第i個狀態機命令選擇的值產生不同意見。選舉一個單一領導者只是爲了確保取得進展。

如果服務器集合可以改變,那麼必須有某種方法來確定哪些服務器實現了一致性算法的哪些實例。最簡單的方法是通過狀態機本身。當前的服務器集合可以成爲狀態的一部分,並且可以使用普通的狀態機命令進行改變。我們可以允許領導者提前獲得a個命令,通過讓服務器集合執行一致算法的實例i+a(由第i個狀態機命令執行之後的狀態來確定)。這允許簡單地實現任意複雜的重新配置算法。

參考文獻

  1. Michael J. Fischer, Nancy Lynch, and Michael S. Paterson. Impossibility of distributed consensus with one faulty process. Journal of the ACM, 32(2):374–382, April 1985.
  2. Idit Keidar and Sergio Rajsbaum. On the cost of fault-tolerant consensus when there are no faults—a tutorial. TechnicalReport MIT-LCS-TR-821, Laboratory for Computer Science, Massachusetts Institute Technology, Cambridge, MA, 02139, May 2001. also published in SIGACT News 32(2) (June 2001).
  3. Leslie Lamport. The implementation of reliable distributed multiprocess systems. Computer Networks, 2:95–114, 1978.
  4. Leslie Lamport. Time, clocks, and the ordering of events in a distributed system. Communications of the ACM, 21(7):558–565, July 1978.
  5. Leslie Lamport. The part-time parliament. ACM Transactions on Computer Systems, 16(2):133–169, May 1998.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章