分佈式一致性算法:Raft 算法(Raft 論文翻譯)

Raft 算法是可以用來替代 Paxos 算法的分佈式一致性算法,而且 raft 算法比 Paxos 算法更易懂且更容易實現。本文對 raft 論文進行翻譯,希望能有助於讀者更方便地理解 raft 的思想。如果對 Paxos 算法感興趣,可以看我的另一篇文章:分佈式系列文章——Paxos算法原理與推導

摘要

Raft 是用來管理複製日誌(replicated log)的一致性協議。它跟 multi-Paxos 作用相同,效率也相當,但是它的組織結構跟 Paxos 不同。這使得 Raft 比 Paxos 更容易理解並且更容易在工程實踐中實現。爲了使 Raft 協議更易懂,Raft 將一致性的關鍵元素分開,如 leader 選舉、日誌複製和安全性,並且它實施更強的一致性以減少必須考慮的狀態的數量。用戶研究的結果表明,Raft 比 Paxos 更容易學習。 Raft 還包括一個用於變更集羣成員的新機制,它使用重疊的大多數(overlapping majorities)來保證安全性。

1 介紹

一致性算法允許多臺機器作爲一個集羣協同工作,並且在其中的某幾臺機器出故障時集羣仍然能正常工作。 正因爲如此,一致性算法在建立可靠的大規模軟件系統方面發揮了關鍵作用。 在過去十年中,Paxos [15,16] 主導了關於一致性算法的討論:大多數一致性的實現都是基於 Paxos 或受其影響,Paxos 已成爲用於教授學生一致性相關知識的主要工具。

不幸的是,Paxos 實在是太難以理解,儘管許多人一直在努力嘗試使其更易懂。 此外,其架構需要複雜的改變來支持實際系統。 結果是,系統開發者和學生都在與 Paxos 鬥爭。

在我們自己與 Paxos 鬥爭之後,我們開始着手尋找一個新的一致性算法,可以爲系統開發和教學提供更好的基礎。 我們的方法是不尋常的,因爲我們的主要目標是可理解性:我們可以爲實際系統定義一個一致性算法,並以比 Paxos 更容易學習的方式描述它嗎?在該算法的設計過程中,重要的不僅是如何讓該算法起作用,還有清晰地知道該算法爲什麼會起作用。

這項工作的結果是一個稱爲 Raft 的一致性算法。 在設計 Raft 時,我們使用了特定的技術來提高可理解性,包括分解(Raft 分離 leader 選舉,日誌複製和安全)和狀態空間減少(相對於 Paxos ,Raft 減少了不確定性程度和服務器之間彼此不一致的方式 )。 一項針對兩個大學的 43 名學生的用戶研究表明,Raft 比 Paxos 更容易理解:在學習兩種算法後,其中 33 名學生能夠更好地回答關於 Raft 的問題。

Raft 在許多方面類似於現有的一致性算法(尤其是 Oki 和 Liskov 的 Viewstamped Replication [29,22]),但它有幾個新特性:

  • Strong leader:在 Raft 中,日誌條目(log entries)只從 leader 流向其他服務器。 這簡化了複製日誌的管理,使得 raft 更容易理解。

  • Leader 選舉:Raft 使用隨機計時器進行 leader 選舉。 這隻需在任何一致性算法都需要的心跳(heartbeats)上增加少量機制,同時能夠簡單快速地解決衝突。

  • 成員變更:Raft 使用了一種新的聯合一致性方法,其中兩個不同配置的大多數在過渡期間重疊。 這允許集羣在配置更改期間繼續正常運行。

我們認爲,Raft 優於 Paxos 和其他一致性算法,不僅在教學方面,在工程實現方面也是。 它比其他算法更簡單且更易於理解; 它被描述得十分詳細足以滿足實際系統的需要; 它有多個開源實現,並被多家公司使用; 它的安全性已被正式規定和驗證; 它的效率與其他算法相當。

本文的剩餘部分介紹了複製狀態機問題(第 2 節),討論了 Paxos 的優點和缺點(第3節),描述了我們實現易理解性的方法(第 4 節),提出了 Raft 一致性算法(第 5-8 節),評估 Raft(第 9 節),並討論了相關工作(第 10 節)。

2 複製狀態機

一致性算法是在複製狀態機[37]的背景下產生的。 在這種方法中,一組服務器上的狀態機計算相同狀態的相同副本,並且即使某些服務器宕機,也可以繼續運行。

複製狀態機用於解決分佈式系統中的各種容錯問題。 例如,具有單個 leader 的大規模系統,如 GFS [8],HDFS [38] 和 RAMCloud [33] ,通常使用單獨的複製狀態機來進行 leader 選舉和存儲 leader 崩潰後重新選舉需要的配置信息。Chubby [2] 和 ZooKeeper [11] 都是複製狀態機。

複製狀態機通常使用複製日誌實現,如圖 1 所示。每個服務器存儲一個包含一系列命令的日誌,其狀態機按順序執行日誌中的命令。 每個日誌中命令都相同並且順序也一樣,因此每個狀態機處理相同的命令序列。 這樣就能得到相同的狀態和相同的輸出序列。

圖1

一致性算法的工作就是保證複製日誌的一致性。 每臺服務器上的一致性模塊接收來自客戶端的命令,並將它們添加到其日誌中。 它與其他服務器上的一致性模塊通信,以確保每個日誌最終以相同的順序包含相同的命令,即使有一些服務器失敗。 一旦命令被正確複製,每個服務器上的狀態機按日誌順序處理它們,並將輸出返回給客戶端。 這樣就形成了高可用的複製狀態機。

實際系統中的一致性算法通常具有以下屬性:

  • 它們確保在所有非拜占庭條件下(包括網絡延遲,分區和數據包丟失,重複和亂序)的安全性(不會返回不正確的結果)。

  • 只要任何大多數(過半)服務器都可以運行,並且可以相互通信和與客戶通信,一致性算法就可用。 因此,五臺服務器的典型集羣可以容忍任何兩臺服務器的故障。 假設服務器突然宕機,它們可以稍後從狀態恢復並重新加入羣集。

  • 它們不依賴於時序來確保日誌的一致性:錯誤的時鐘和極端消息延遲在最壞的情況下會導致可用性問題(譯者注:言外之意是可以保證一致性)。

  • 在通常情況下,只要集羣的大部分(過半服務器)已經響應了單輪遠程過程調用,命令就可以完成; 少數(一半以下)慢服務器不會影響整個系統性能。

3 Paxos 存在的問題

在過去十年裏,Leslie Lamport 的 Paxos 協議[15]幾乎成爲一致性的同義詞:它是課堂上教授最多的一致性協議,並且大多數一致性的實現也以它爲起點。 Paxos 首先定義了能夠在單個決策(例如單個複製日誌條目)上達成一致的協議。 我們將這個子集稱爲 single-decree Paxos。 然後 Paxos 組合該協議的多個實例以促進一系列決策,例如日誌(multi-Paxos)。 Paxos能夠確保安全性和活性,並且支持集羣成員的變更。它的正確性已被證明,並且在正常情況下是高效的。

不幸的是,Paxos 有兩個顯著的缺點。 第一個缺點是 Paxos 非常難以理解。 Paxos 的描述晦澀難懂,臭名昭著(譯者注:《The Part-time Parliament》比較晦澀難懂,但是《Paxos Made Simple》就比較容易理解); 很少有人成功地理解它,即使能理解也必須付出巨大的努力。 因此,已有幾個嘗試用更簡單的方式來描述 Paxos [16,20,21] 。 這些描述集中在 single-degree Paxos ,但它們仍然具有挑戰性。 在對 NSDI 2012 參會者的非正式調查中,我們發現很少有人喜歡 Paxos ,即使是經驗豐富的研究人員。 我們自己也跟 Paxos 進行了艱苦的鬥爭; 我們也無法完全理解整個協議,直到閱讀了幾個更簡單的描述和自己設計替代 Paxos 的協議,整個過程花了將近一年。

Paxos 晦澀難懂的原因是作者選擇了single-degree Paxos作爲基礎。Single-decree Paxos 分成兩個階段,這兩個階段沒有簡單直觀的說明,並且不能被單獨理解。因此,很難理解爲什麼該算法能起作用。Multi-Paxos 的合成規則又增加了許多複雜性。我們相信,對多個決定(日誌而不是單個日誌條目)達成一致的總體問題可以用其他更直接和更明顯的方式進行分解。

Paxos的第二個問題是它不能爲構建實際的實現提供良好的基礎。 一個原因是沒有針對 multi-Paxos 的廣泛同意的算法。 Lamport的描述主要是關於 single-decree Paxos; 他描述了 multi-Paxos 的可能方法,但缺少許多細節。 已經有幾個嘗試來具體化和優化 Paxos ,例如[26],[39]和[13],但這些彼此各不相同並且跟 Lamport 描述的也不同。 像Chubby [4] 這樣的系統已經實現了類 Paxos(Paxos-like)算法,但大多數情況下,它們的細節並沒有公佈。

此外,Paxos 的架構對於構建實際系統來說是一個糟糕的設計,這是 single-decree 分解的另一個結果。 例如,獨立地選擇日誌條目集合,然後再將它們合併到順序日誌中幾乎沒有任何好處,這隻會增加複雜性。 圍繞日誌設計系統是更簡單和有效的方法,新日誌條目按照約束順序地添加到日誌中。 Paxos 的做法適用於只需要做一次決策的情況,如果需要做一系列決策,更簡單和快速的方法是先選擇一個 leader ,然後讓該 leader 協調這些決策。

因此,實際的系統跟 Paxos 相差很大。幾乎所有的實現都是從 Paxos 開始,然後發現很多實現上的難題,接着就開發了一種和 Paxos 完全不一樣的架構。這樣既費時又容易出錯,而且 Paxos 本身晦澀難懂使得該問題更加嚴重。Paxos 的公式可能可以很好地證明它的正確性,但是現實的系統和 Paxos 差別是如此之大,以至於這些證明並沒有什麼太大的價值。下面來自 Chubby 作者的評論非常典型:

在Paxos算法描述和實現現實系統之間有着巨大的鴻溝。最終的系統往往建立在一個還未被證明的協議之上。

由於以上問題,我們得出的結論是 Paxos 算法沒有爲系統實踐和教學提供一個良好的基礎。考慮到一致性問題在大規模軟件系統中的重要性,我們決定嘗試設計一個能夠替代 Paxos 並且具有更好特性的一致性算法。Raft算法就是這次實驗的結果。

4 爲可理解性而設計

在設計 Raft 算法過程中我們有幾個目標:它必須提供一個完整的實際的系統實現基礎,這樣才能大大減少開發者的工作;它必須在任何情況下都是安全的並且在典型的應用條件下是可用的;並且在正常情況下是高效的。但是我們最重要的目標也是最大的挑戰是可理解性。它必須保證能夠被大多數人容易地理解。另外,它必須能夠讓人形成直觀的認識,這樣系統的構建者才能夠在現實中進行擴展。

在設計 Raft 算法的時候,很多情況下我們需要在多個備選方案中進行選擇。在這種情況下,我們基於可理解性來評估備選方案:解釋各個備選方案的難道有多大(例如,Raft 的狀態空間有多複雜,是否有微妙的含義)?對於一個讀者而言,完全理解這個方案和含義是否容易?

我們意識到這樣的分析具有高度的主觀性;但是我們使用了兩種通用的技術來解決這個問題。第一個技術就是衆所周知的問題分解:只要有可能,我們就將問題分解成幾個相對獨立的,可被解決的、可解釋的和可理解的子問題。例如,Raft 算法被我們分成 leader 選舉,日誌複製,安全性和成員變更幾個部分。

我們使用的第二個方法是通過減少狀態的數量來簡化狀態空間,使得系統更加連貫並且儘可能消除不確定性。特別的,所有的日誌是不允許有空洞的,並且 Raft 限制了使日誌之間不一致的方式。儘管在大多數情況下我們都試圖去消除不確定性,但是在某些情況下不確定性可以提高可理解性。特別是,隨機化方法雖然引入了不確定性,但是他們往往能夠通過使用相近的方法處理可能的選擇來減少狀態空間。我們使用隨機化來簡化 Raft 中的 leader 選舉算法。

5 Raft 一致性算法

Raft 是一種用來管理第 2 節中描述的複製日誌的算法。圖 2 是該算法的濃縮,可用作參考,圖 3 列舉了該算法的一些關鍵特性。圖中的這些內容將在剩下的章節中逐一介紹。

圖2

圖3

Raft 通過首先選舉一個 distinguished leader,然後讓它全權負責管理複製日誌來實現一致性。Leader 從客戶端接收日誌條目,把日誌條目複製到其他服務器上,並且在保證安全性的時候通知其他服務器將日誌條目應用到他們的狀態機中。擁有一個 leader 大大簡化了對複製日誌的管理。例如,leader 可以決定新的日誌條目需要放在日誌中的什麼位置而不需要和其他服務器商議,並且數據都是從 leader 流向其他服務器。leader 可能宕機,也可能和其他服務器斷開連接,這時一個新的 leader 會被選舉出來。

通過選舉一個 leader 的方式,Raft 將一致性問題分解成了三個相對獨立的子問題,這些問題將會在接下來的子章節中進行討論:

  • Leader 選舉:當前的 leader 宕機時,一個新的 leader 必須被選舉出來。(5.2 節)

  • 日誌複製:Leader 必須從客戶端接收日誌條目然後複製到集羣中的其他節點,並且強制要求其他節點的日誌和自己的保持一致。

  • 安全性:Raft 中安全性的關鍵是圖 3 中狀態機的安全性:如果有任何的服務器節點已經應用了一個特定的日誌條目到它的狀態機中,那麼其他服務器節點不能在同一個日誌索引位置應用一條不同的指令。章節 5.4 闡述了 Raft 算法是如何保證這個特性的;該解決方案在選舉機制(5.2 節)上增加了額外的限制。

在展示一致性算法之後,本章節將討論可用性的一些問題以及時序在系統中的作用。

5.1 Raft 基礎

一個 Raft 集羣包含若干個服務器節點;通常是 5 個,這樣的系統可以容忍 2 個節點的失效。在任何時刻,每一個服務器節點都處於這三個狀態之一:leader、follower 或者 candidate 。在正常情況下,集羣中只有一個 leader 並且其他的節點全部都是 follower 。Follower 都是被動的:他們不會發送任何請求,只是簡單的響應來自 leader 和 candidate 的請求。Leader 處理所有的客戶端請求(如果一個客戶端和 follower 通信,follower 會將請求重定向給 leader)。第三種狀態,candidate ,是用來選舉一個新的 leader(章節 5.2)。圖 4 展示了這些狀態和他們之間的轉換關係;這些轉換關係在接下來會進行討論。

圖4

Raft 把時間分割成任意長度的任期(term),如圖 5 所示。任期用連續的整數標記。每一段任期從一次選舉開始,一個或者多個 candidate 嘗試成爲 leader 。如果一個 candidate 贏得選舉,然後他就在該任期剩下的時間裏充當 leader 。在某些情況下,一次選舉無法選出 leader 。在這種情況下,這一任期會以沒有 leader 結束;一個新的任期(包含一次新的選舉)會很快重新開始。Raft 保證了在任意一個任期內,最多隻有一個 leader 。

圖5

不同的服務器節點觀察到的任期轉換的次數可能不同,在某些情況下,一個服務器節點可能沒有看到 leader 選舉過程或者甚至整個任期全程。任期在 Raft 算法中充當邏輯時鐘的作用,這使得服務器節點可以發現一些過期的信息比如過時的 leader 。每一個服務器節點存儲一個當前任期號,該編號隨着時間單調遞增。服務器之間通信的時候會交換當前任期號;如果一個服務器的當前任期號比其他的小,該服務器會將自己的任期號更新爲較大的那個值。如果一個 candidate 或者 leader 發現自己的任期號過期了,它會立即回到 follower 狀態。如果一個節點接收到一個包含過期的任期號的請求,它會直接拒絕這個請求。

Raft 算法中服務器節點之間使用 RPC 進行通信,並且基本的一致性算法只需要兩種類型的 RPC。請求投票(RequestVote) RPC 由 candidate 在選舉期間發起(章節 5.2),追加條目(AppendEntries)RPC 由 leader 發起,用來複制日誌和提供一種心跳機制(章節 5.3)。第 7 節爲了在服務器之間傳輸快照增加了第三種 RPC。當服務器沒有及時的收到 RPC 的響應時,會進行重試, 並且他們能夠並行的發起 RPC 來獲得最佳的性能。

5.2 Leader 選舉

Raft 使用一種心跳機制來觸發 leader 選舉。當服務器程序啓動時,他們都是 follower 。一個服務器節點只要能從 leader 或 candidate 處接收到有效的 RPC 就一直保持 follower 狀態。Leader 週期性地向所有 follower 發送心跳(不包含日誌條目的 AppendEntries RPC)來維持自己的地位。如果一個 follower 在一段選舉超時時間內沒有接收到任何消息,它就假設系統中沒有可用的 leader ,然後開始進行選舉以選出新的 leader 。

要開始一次選舉過程,follower 先增加自己的當前任期號並且轉換到 candidate 狀態。然後投票給自己並且並行地向集羣中的其他服務器節點發送 RequestVote RPC(讓其他服務器節點投票給它)。Candidate 會一直保持當前狀態直到以下三件事情之一發生:(a) 它自己贏得了這次的選舉(收到過半的投票),(b) 其他的服務器節點成爲 leader ,(c) 一段時間之後沒有任何獲勝者。這些結果會在下面的章節裏分別討論。

當一個 candidate 獲得集羣中過半服務器節點針對同一個任期的投票,它就贏得了這次選舉併成爲 leader 。對於同一個任期,每個服務器節點只會投給一個 candidate ,按照先來先服務(first-come-first-served)的原則(注意:5.4 節在投票上增加了額外的限制)。要求獲得過半投票的規則確保了最多隻有一個 candidate 贏得此次選舉(圖 3 中的選舉安全性)。一旦 candidate 贏得選舉,就立即成爲 leader 。然後它會向其他的服務器節點發送心跳消息來確定自己的地位並阻止新的選舉。

在等待投票期間,candidate 可能會收到另一個聲稱自己是 leader 的服務器節點發來的 AppendEntries RPC 。如果這個 leader 的任期號(包含在RPC中)不小於 candidate 當前的任期號,那麼 candidate 會承認該 leader 的合法地位並回到 follower 狀態。 如果 RPC 中的任期號比自己的小,那麼 candidate 就會拒絕這次的 RPC 並且繼續保持 candidate 狀態。

第三種可能的結果是 candidate 既沒有贏得選舉也沒有輸:如果有多個 follower 同時成爲 candidate ,那麼選票可能會被瓜分以至於沒有 candidate 贏得過半的投票。當這種情況發生時,每一個候選人都會超時,然後通過增加當前任期號來開始一輪新的選舉。然而,如果沒有其他機制的話,該情況可能會無限重複。

Raft 算法使用隨機選舉超時時間的方法來確保很少發生選票瓜分的情況,就算髮生也能很快地解決。爲了阻止選票一開始就被瓜分,選舉超時時間是從一個固定的區間(例如 150-300 毫秒)隨機選擇。這樣可以把服務器都分散開以至於在大多數情況下只有一個服務器會選舉超時;然後該服務器贏得選舉並在其他服務器超時之前發送心跳。同樣的機制被用來解決選票被瓜分的情況。每個 candidate 在開始一次選舉的時候會重置一個隨機的選舉超時時間,然後一直等待直到選舉超時;這樣減小了在新的選舉中再次發生選票瓜分情況的可能性。9.3 節展示了該方案能夠快速地選出一個 leader 。

選舉的例子可以很好地展示可理解性是如何指導我們選擇設計方案的。起初我們打算使用一種等級系統(ranking system):每一個 candidate 都被賦予一個唯一的等級(rank),等級用來在競爭的 candidate 之間進行選擇。如果一個 candidate 發現另一個 candidate 擁有更高的等級,它就會回到 follower 狀態,這樣高等級的 candidate 能夠更加容易地贏得下一次選舉。但是我們發現這種方法在可用性方面會有一下小問題。我們對該算法進行了多次調整,但是每次調整之後都會有新的小問題。最終我們認爲隨機重試的方法更加顯然且易於理解。

5.3 日誌複製

Leader 一旦被選舉出來,就開始爲客戶端請求提供服務。客戶端的每一個請求都包含一條將被複制狀態機執行的指令。Leader 把該指令作爲一個新的條目追加到日誌中去,然後並行的發起 AppendEntries RPC 給其他的服務器,讓它們複製該條目。當該條目被安全地複製(下面會介紹),leader 會應用該條目到它的狀態機中(狀態機執行該指令)然後把執行的結果返回給客戶端。如果 follower 崩潰或者運行緩慢,或者網絡丟包,leader 會不斷地重試 AppendEntries RPC(即使已經回覆了客戶端)直到所有的 follower 最終都存儲了所有的日誌條目。

日誌以圖 6 展示的方式組織。每個日誌條目存儲一條狀態機指令和 leader 收到該指令時的任期號。任期號用來檢測多個日誌副本之間的不一致情況,同時也用來保證圖 3 中的某些性質。每個日誌條目都有一個整數索引值來表明它在日誌中的位置。

圖6

Leader 決定什麼時候把日誌條目應用到狀態機中是安全的;這種日誌條目被稱爲已提交的。Raft 算法保證所有已提交的日誌條目都是持久化的並且最終會被所有可用的狀態機執行。一旦創建該日誌條目的 leader 將它複製到過半的服務器上,該日誌條目就會被提交(例如在圖 6 中的條目 7)。同時,leader 日誌中該日誌條目之前的所有日誌條目也都會被提交,包括由其他 leader 創建的條目。5.4 節討論在 leader 變更之後應用該規則的一些細節,並且證明了這種提交的規則是安全的。Leader 追蹤將會被提交的日誌條目的最大索引,未來的所有 AppendEntries RPC 都會包含該索引,這樣其他的服務器才能最終知道哪些日誌條目需要被提交。Follower 一旦知道某個日誌條目已經被提交就會將該日誌條目應用到自己的本地狀態機中(按照日誌的順序)。

我們設計了 Raft 日誌機制來維持不同服務器之間日誌高層次的一致性。這麼做不僅簡化了系統的行爲也使得系統行爲更加可預測,同時該機制也是保證安全性的重要組成部分。Raft 維護着以下特性,這些同時也構成了圖 3 中的日誌匹配特性:

  • 如果不同日誌中的兩個條目擁有相同的索引和任期號,那麼他們存儲了相同的指令。

  • 如果不同日誌中的兩個條目擁有相同的索引和任期號,那麼他們之前的所有日誌條目也都相同。

Leader 在特定的任期號內的一個日誌索引處最多創建一個日誌條目,同時日誌條目在日誌中的位置也從來不會改變。該點保證了上面的第一條特性。第二個特性是由 AppendEntries RPC 執行一個簡單的一致性檢查所保證的。在發送 AppendEntries RPC 的時候,leader 會將前一個日誌條目的索引位置和任期號包含在裏面。如果 follower 在它的日誌中找不到包含相同索引位置和任期號的條目,那麼他就會拒絕該新的日誌條目。一致性檢查就像一個歸納步驟:一開始空的日誌狀態肯定是滿足 Log Matching Property(日誌匹配特性) 的,然後一致性檢查保證了日誌擴展時的日誌匹配特性。因此,每當 AppendEntries RPC 返回成功時,leader 就知道 follower 的日誌一定和自己相同(從第一個日誌條目到最新條目)。

正常操作期間,leader 和 follower 的日誌保持一致,所以 AppendEntries RPC 的一致性檢查從來不會失敗。然而,leader 崩潰的情況會使日誌處於不一致的狀態(老的 leader 可能還沒有完全複製它日誌裏的所有條目)。這種不一致會在一系列的 leader 和 follower 崩潰的情況下加劇。圖 7 展示了在什麼情況下 follower 的日誌可能和新的 leader 的日誌不同。Follower 可能缺少一些在新 leader 中有的日誌條目,也可能擁有一些新 leader 沒有的日誌條目,或者同時發生。缺失或多出日誌條目的情況可能會涉及到多個任期。

圖7

圖 7:當一個 leader 成功當選時(最上面那條日誌),follower 可能是(a-f)中的任何情況。每一個盒子表示一個日誌條目;裏面的數字表示任期號。Follower 可能會缺少一些日誌條目(a-b),可能會有一些未被提交的日誌條目(c-d),或者兩種情況都存在(e-f)。例如,場景 f 可能這樣發生,f 對應的服務器在任期 2 的時候是 leader ,追加了一些日誌條目到自己的日誌中,一條都還沒提交(commit)就崩潰了;該服務器很快重啓,在任期 3 重新被選爲 leader,又追加了一些日誌條目到自己的日誌中;在這些任期 2 和任期 3 中的日誌都還沒被提交之前,該服務器又宕機了,並且在接下來的幾個任期裏一直處於宕機狀態。

在 Raft 算法中,leader 通過強制 follower 複製它的日誌來解決不一致的問題。這意味着 follower 中跟 leader 衝突的日誌條目會被 leader 的日誌條目覆蓋。5.4 節會證明通過增加一個限制可以保證安全性。

要使得 follower 的日誌跟自己一致,leader 必須找到兩者達成一致的最大的日誌條目(索引最大),刪除 follower 日誌中從那個點之後的所有日誌條目,並且將自己從那個點之後的所有日誌條目發送給 follower 。所有的這些操作都發生在對 AppendEntries RPCs 中一致性檢查的回覆中。Leader 針對每一個 follower 都維護了一個 nextIndex ,表示 leader 要發送給 follower 的下一個日誌條目的索引。當選出一個新 leader 時,該 leader 將所有 nextIndex 的值都初始化爲自己最後一個日誌條目的 index 加1(圖 7 中的 11)。如果 follower 的日誌和 leader 的不一致,那麼下一次 AppendEntries RPC 中的一致性檢查就會失敗。在被 follower 拒絕之後,leaer 就會減小 nextIndex 值並重試 AppendEntries RPC 。最終 nextIndex 會在某個位置使得 leader 和 follower 的日誌達成一致。此時,AppendEntries RPC 就會成功,將 follower 中跟 leader 衝突的日誌條目全部刪除然後追加 leader 中的日誌條目(如果有需要追加的日誌條目的話)。一旦 AppendEntries RPC 成功,follower 的日誌就和 leader 一致,並且在該任期接下來的時間裏保持一致。

如果想要的話,該協議可以被優化來減少被拒絕的 AppendEntries RPC 的個數。例如,當拒絕一個 AppendEntries RPC 的請求的時候,follower 可以包含衝突條目的任期號和自己存儲的那個任期的第一個 index 。藉助這些信息,leader 可以跳過那個任期內所有衝突的日誌條目來減小 nextIndex;這樣就變成每個有衝突日誌條目的任期需要一個 AppendEntries RPC 而不是每個條目一次。在實踐中,我們認爲這種優化是沒有必要的,因爲失敗不經常發生並且也不可能有很多不一致的日誌條目。

通過這種機制,leader 在當權之後就不需要任何特殊的操作來使日誌恢復到一致狀態。Leader 只需要進行正常的操作,然後日誌就能在回覆 AppendEntries 一致性檢查失敗的時候自動趨於一致。Leader 從來不會覆蓋或者刪除自己的日誌條目(圖 3 的 Leader Append-Only 屬性)。

這樣的日誌複製機制展示了第 2 節中描述的一致性特性:只要過半的服務器能正常運行,Raft 就能夠接受,複製並應用新的日誌條目;在正常情況下,新的日誌條目可以在一個 RPC 來回中被複制給集羣中的過半機器;並且單個運行慢的 follower 不會影響整體的性能。

5.4 安全性

前面的章節裏描述了 Raft 算法是如何進行 leader 選舉和日誌複製的。然而,到目前爲止描述的機制並不能充分地保證每一個狀態機會按照相同的順序執行相同的指令。例如,一個 follower 可能會進入不可用狀態,在此期間,leader 可能提交了若干的日誌條目,然後這個 follower 可能會被選舉爲 leader 並且用新的日誌條目覆蓋這些日誌條目;結果,不同的狀態機可能會執行不同的指令序列。

這節通過對 leader 選舉增加一個限制來完善 Raft 算法。這一限制保證了對於給定的任意任期號, leader 都包含了之前各個任期所有被提交的日誌條目(圖 3 中的 Leader Completeness 性質)。有了這一 leader 選舉的限制,我們也使得提交規則更加清晰。最後,我們展示了對於 Leader Completeness 性質的簡要證明並且說明該性質是如何領導複製狀態機執行正確的行爲的。

5.4.1 選舉限制

在任何基於 leader 的一致性算法中,leader 最終都必須存儲所有已經提交的日誌條目。在某些一致性算法中,例如 Viewstamped Replication[22],一開始並沒有包含所有已經提交的日誌條目的服務器也可能被選爲 leader 。這種算法包含一些額外的機制來識別丟失的日誌條目並將它們傳送給新的 leader ,要麼是在選舉階段要麼在之後很快進行。不幸的是,這種方法會導致相當大的額外的機制和複雜性。Raft 使用了一種更加簡單的方法,它可以保證新 leader 在當選時就包含了之前所有任期號中已經提交的日誌條目,不需要再傳送這些日誌條目給新 leader 。這意味着日誌條目的傳送是單向的,只從 leader 到 follower,並且 leader 從不會覆蓋本地日誌中已經存在的條目。

Raft 使用投票的方式來阻止 candidate 贏得選舉除非該 candidate 包含了所有已經提交的日誌條目。候選人爲了贏得選舉必須與集羣中的過半節點通信,這意味着至少其中一個服務器節點包含了所有已提交的日誌條目。如果 candidate 的日誌至少和過半的服務器節點一樣新(接下來會精確地定義“新”),那麼他一定包含了所有已經提交的日誌條目。RequestVote RPC 執行了這樣的限制: RPC 中包含了 candidate 的日誌信息,如果投票者自己的日誌比 candidate 的還新,它會拒絕掉該投票請求。

Raft 通過比較兩份日誌中最後一條日誌條目的索引值和任期號來定義誰的日誌比較新。如果兩份日誌最後條目的任期號不同,那麼任期號大的日誌更新。如果兩份日誌最後條目的任期號相同,那麼日誌較長的那個更新。

5.4.2 提交之前任期內的日誌條目

如同 5.3 節描述的那樣,一旦當前任期內的某個日誌條目已經存儲到過半的服務器節點上,leader 就知道該日誌條目已經被提交了。如果某個 leader 在提交某個日誌條目之前崩潰了,以後的 leader 會試圖完成該日誌條目的複製。然而,如果是之前任期內的某個日誌條目已經存儲到過半的服務器節點上,leader 也無法立即斷定該日誌條目已經被提交了。圖 8 展示了一種情況,一個已經被存儲到過半節點上的老日誌條目,仍然有可能會被未來的 leader 覆蓋掉。

圖8

圖 8:如圖的時間序列展示了爲什麼 leader 無法判斷老的任期號內的日誌是否已經被提交。在 (a) 中,S1 是 leader ,部分地複製了索引位置 2 的日誌條目。在 (b) 中,S1 崩潰了,然後 S5 在任期 3 中通過 S3、S4 和自己的選票贏得選舉,然後從客戶端接收了一條不一樣的日誌條目放在了索引 2 處。然後到 (c),S5 又崩潰了;S1 重新啓動,選舉成功,繼續複製日誌。此時,來自任期 2 的那條日誌已經被複制到了集羣中的大多數機器上,但是還沒有被提交。如果 S1 在 (d) 中又崩潰了,S5 可以重新被選舉成功(通過來自 S2,S3 和 S4 的選票),然後覆蓋了他們在索引 2 處的日誌。但是,在崩潰之前,如果 S1 在自己的任期裏複製了日誌條目到大多數機器上,如 (e) 中,然後這個條目就會被提交(S5 就不可能選舉成功)。 在這種情況下,之前的所有日誌也被提交了。

爲了消除圖 8 中描述的問題,Raft 永遠不會通過計算副本數目的方式來提交之前任期內的日誌條目。只有 leader 當前任期內的日誌條目才通過計算副本數目的方式來提交;一旦當前任期的某個日誌條目以這種方式被提交,那麼由於日誌匹配特性,之前的所有日誌條目也都會被間接地提交。在某些情況下,領導人可以安全地斷定一個老的日誌條目已經被提交(例如,如果該條目已經存儲到所有服務器上),但是 Raft 爲了簡化問題使用了一種更加保守的方法。

Raft 會在提交規則上增加額外的複雜性是因爲當 leader 複製之前任期內的日誌條目時,這些日誌條目都保留原來的任期號。在其他的一致性算法中,如果一個新的 leader 要重新複製之前的任期裏的日誌時,它必須使用當前新的任期號。Raft 的做法使得更加容易推導出(reason about)日誌條目,因爲他們自始至終都使用同一個任期號。另外,和其他的算法相比,Raft 中的新 leader 只需要發送更少的日誌條目(其他算法中必須在它們被提交之前發送更多的冗餘日誌條目來給它們重新編號)。

5.4.3 安全性論證

在給出了完整的 Raft 算法之後,我們現在可以更加精確的討論領導人完整性特性(Leader Completeness Prop-erty)(這一討論基於 9.2 節的安全性證明)。我們假設領導人完全性特性是不滿足的,然後我們推出矛盾來。假設任期 T 的 leader(leader T)在任期內提交了一個日誌條目,但是該日誌條目沒有被存儲到未來某些任期的 leader 中。假設 U 是大於 T 的沒有存儲該日誌條目的最小任期號。

圖9

圖 9:如果 S1 (任期 T 的 leader)在它的任期裏提交了一個新的日誌條目,然後 S5 在之後的任期 U 裏被選舉爲 leader ,那麼肯定至少會有一個節點,如 S3,既接收了來自 S1 的日誌條目,也給 S5 投票了。

  1. U 一定在剛成爲 leader 的時候就沒有那條被提交的日誌條目了(leader 從不會刪除或者覆蓋任何條目)。

  2. Leader T 複製該日誌條目給集羣中的過半節點,同時,leader U 從集羣中的過半節點贏得了選票。因此,至少有一個節點(投票者)同時接受了來自 leader T 的日誌條目和給 leader U 投票了,如圖 9。該投票者是產生矛盾的關鍵。

  3. 該投票者必須在給 leader U 投票之前先接受了從 leader T 發來的已經被提交的日誌條目;否則它就會拒絕來自 leader T 的 AppendEntries 請求(因爲此時它的任期號會比 T 大)。

  4. 該投票者在給 leader U 投票時依然保有這該日誌條目,因爲任何 U 、T 之間的 leader 都包含該日誌條目(根據上述的假設),leader 從不會刪除條目,並且 follower 只有跟 leader 衝突的時候纔會刪除條目。

  5. 該投票者把自己選票投給 leader U 時,leader U 的日誌必須至少和投票者的一樣新。這就導致了以下兩個矛盾之一。

  6. 首先,如果該投票者和 leader U 的最後一個日誌條目的任期號相同,那麼 leader U 的日誌至少和該投票者的一樣長,所以 leader U 的日誌一定包含該投票者日誌中的所有日誌條目。這是一個矛盾,因爲該投票者包含了該已被提交的日誌條目,但是在上述的假設裏,leader U 是不包含的。

  7. 否則,leader U 的最後一個日誌條目的任期號就必須比該投票者的大了。此外,該任期號也比 T 大,因爲該投票者的最後一個日誌條目的任期號至少和 T 一樣大(他包含了來自任期 T 的已提交的日誌)。創建了 leader U 最後一個日誌條目的之前的 leader 一定已經包含了該已被提交的日誌條目(根據上述假設,leader U 是第一個不包含該日誌條目的 leader)。所以,根據日誌匹配特性,leader U 一定也包含該已被提交的日誌條目,這裏產生了矛盾。

  8. 因此,所有比 T 大的任期的 leader 一定都包含了任期 T 中提交的所有日誌條目。

  9. 日誌匹配特性保證了未來的 leader 也會包含被間接提交的日誌條目,例如圖 8 (d) 中的索引 2。

通過 Leader Completeness 特性,我們就能證明圖 3 中的狀態機安全特性,即如果某個服務器已經將某個給定的索引處的日誌條目應用到自己的狀態機裏了,那麼其他的服務器就不會在相同的索引處應用一個不同的日誌條目。在一個服務器應用一個日誌條目到自己的狀態機中時,它的日誌和 leader 的日誌從開始到該日誌條目都相同,並且該日誌條目必須被提交。現在考慮如下最小任期號:某服務器在該任期號中某個特定的索引處應用了一個日誌條目;日誌完整性特性保證擁有更高任期號的 leader 會存儲相同的日誌條目,所以之後任期裏服務器應用該索引處的日誌條目也會是相同的值。因此,狀態機安全特性是成立的。

最後,Raft 要求服務器按照日誌索引順序應用日誌條目。再加上狀態機安全特性,這就意味着所有的服務器都會按照相同的順序應用相同的日誌條目到自己的狀態機中。

5.5 Follower 和 candidate 崩潰

到目前爲止,我們只關注了 leader 崩潰的情況。Follower 和 candidate 崩潰後的處理方式比 leader 崩潰要簡單的多,並且兩者的處理方式是相同的。如果 follower 或者 candidate 崩潰了,那麼後續發送給他們的 RequestVote 和 AppendEntries RPCs 都會失敗。Raft 通過無限的重試來處理這種失敗;如果崩潰的機器重啓了,那麼這些 RPC 就會成功地完成。如果一個服務器在完成了一個 RPC,但是還沒有響應的時候崩潰了,那麼在它重啓之後就會再次收到同樣的請求。Raft 的 RPCs 都是冪等的,所以這樣的重試不會造成任何傷害。例如,一個 follower 如果收到 AppendEntries 請求但是它的日誌中已經包含了這些日誌條目,它就會直接忽略這個新的請求中的這些日誌條目。

5.6 定時(timing)和可用性

Raft 的要求之一就是安全性不能依賴定時:整個系統不能因爲某些事件運行得比預期快一點或者慢一點就產生錯誤的結果。但是,可用性(系統能夠及時響應客戶端)不可避免的要依賴於定時。例如,當有服務器崩潰時,消息交換的時間就會比正常情況下長,candidate 將不會等待太長的時間來贏得選舉;沒有一個穩定的 leader ,Raft 將無法工作。

Leader 選舉是 Raft 中定時最爲關鍵的方面。 只要整個系統滿足下面的時間要求,Raft 就可以選舉出並維持一個穩定的 leader:

廣播時間(broadcastTime) << 選舉超時時間(electionTimeout) << 平均故障間隔時間(MTBF)

在這個不等式中,廣播時間指的是一個服務器並行地發送 RPCs 給集羣中所有的其他服務器並接收到響應的平均時間;選舉超時時間就是在 5.2 節中介紹的選舉超時時間;平均故障間隔時間就是對於一臺服務器而言,兩次故障間隔時間的平均值。廣播時間必須比選舉超時時間小一個量級,這樣 leader 才能夠可靠地發送心跳消息來阻止 follower 開始進入選舉狀態;再加上隨機化選舉超時時間的方法,這個不等式也使得選票瓜分的情況變得不可能。選舉超時時間需要比平均故障間隔時間小上幾個數量級,這樣整個系統才能穩定地運行。當 leader 崩潰後,整個系統會有大約選舉超時時間不可用;我們希望該情況在整個時間裏只佔一小部分。

廣播時間和平均故障間隔時間是由系統決定的,但是選舉超時時間是我們自己選擇的。Raft 的 RPCs 需要接收方將信息持久化地保存到穩定存儲中去,所以廣播時間大約是 0.5 毫秒到 20 毫秒之間,取決於存儲的技術。因此,選舉超時時間可能需要在 10 毫秒到 500 毫秒之間。大多數的服務器的平均故障間隔時間都在幾個月甚至更長,很容易滿足時間的要求。

6 集羣成員變更

到目前爲止,我們都假設集羣的配置(參與一致性算法的服務器集合)是固定不變的。但是在實踐中,偶爾會改變集羣的配置的,例如替換那些宕機的機器或者改變複製程度。儘管可以通過使整個集羣下線,更新所有配置,然後重啓整個集羣的方式來實現,但是在更改期間集羣會不可用。另外,如果存在手工操作步驟,那麼就會有操作失誤的風險。爲了避免這樣的問題,我們決定將配置變更自動化並將其納入到 Raft 一致性算法中來。

爲了使配置變更機制能夠安全,在轉換的過程中不能夠存在任何時間點使得同一個任期裏可能選出兩個 leader 。不幸的是,任何服務器直接從舊的配置轉換到新的配置的方案都是不安全的。一次性自動地轉換所有服務器是不可能的,所以在轉換期間整個集羣可能劃分成兩個獨立的大多數(見圖 10)。

圖10

圖 10:直接從一種配置轉到另一種配置是不安全的,因爲各個機器會在不同的時候進行轉換。在這個例子中,集羣從 3 臺機器變成了 5 臺。不幸的是,存在這樣的一個時間點,同一個任期裏兩個不同的 leader 會被選出。一個獲得舊配置裏過半機器的投票,一個獲得新配置裏過半機器的投票。

爲了保證安全性,配置變更必須採用一種兩階段方法。目前有很多種兩階段的實現。例如,有些系統(比如,[22])在第一階段停掉舊的配置所以不能處理客戶端請求;然後在第二階段在啓用新的配置。在 Raft 中,集羣先切換到一個過渡的配置,我們稱之爲聯合一致(joint consensus);一旦聯合一致已經被提交了,那麼系統就切換到新的配置上。聯合一致結合了老配置和新配置:

  • 日誌條目被複制給集羣中新、老配置的所有服務器。

  • 新、舊配置的服務器都可以成爲 leader 。

  • 達成一致(針對選舉和提交)需要分別在兩種配置上獲得過半的支持。

聯合一致允許獨立的服務器在不妥協安全性的前提下,在不同的時刻進行配置轉換過程。此外,聯合一致允許集羣在配置變更期間依然響應客戶端請求。

集羣配置在複製日誌中以特殊的日誌條目來存儲和通信;圖 11 展示了配置變更過程。當一個 leader 接收到一個改變配置從 C-old 到 C-new 的請求,它就爲聯合一致將該配置(圖中的 C-old,new)存儲爲一個日誌條目,並以前面描述的方式複製該條目。一旦某個服務器將該新配置日誌條目增加到自己的日誌中,它就會用該配置來做出未來所有的決策(服務器總是使用它日誌中最新的配置,無論該配置日誌是否已經被提交)。這就意味着 leader 會使用 C-old,new 的規則來決定 C-old,new 的日誌條目是什麼時候被提交的。如果 leader 崩潰了,新 leader 可能是在 C-old 配置也可能是在 C-old,new 配置下選出來的,這取決於贏得選舉的 candidate 是否已經接收到了 C-old,new 配置。在任何情況下, C-new 在這一時期都不能做出單方面決定。

一旦 C-old,new 被提交,那麼 C-old 和 C-new 都不能在沒有得到對方認可的情況下做出決定,並且 leader 完整性特性保證了只有擁有 C-old,new 日誌條目的服務器才能被選舉爲 leader 。現在 leader 創建一個描述 C-new 配置的日誌條目並複製到集羣其他節點就是安全的了。此外,新的配置被服務器收到後就會立即生效。當新的配置在 C-new 的規則下被提交,舊的配置就變得無關緊要,同時不使用新配置的服務器就可以被關閉了。如圖 11 所示,任何時刻 C-old 和 C-new 都不能單方面做出決定;這保證了安全性。

在關於配置變更還有三個問題需要解決。第一個問題是,新的服務器開始時可能沒有存儲任何的日誌條目。當這些服務器以這種狀態加入到集羣中,它們需要一段時間來更新來趕上其他服務器,這段它們無法提交新的日誌條目。爲了避免因此而造成的系統短時間的不可用,Raft 在配置變更前引入了一個額外的階段,在該階段,新的服務器以沒有投票權身份加入到集羣中來(leader 也複製日誌給它們,但是考慮過半的時候不用考慮它們)。一旦該新的服務器追趕上了集羣中的其他機器,配置變更就可以按上面描述的方式進行。

第二個問題是,集羣的 leader 可能不是新配置中的一員。在這種情況下,leader 一旦提交了 C-new 日誌條目就會退位(回到 follower 狀態)。這意味着有這樣的一段時間(leader 提交 C-new 期間),leader 管理着一個不包括自己的集羣;它複製着日誌但不把自己算在過半里面。Leader 轉換髮生在 C-new 被提交的時候,因爲這是新配置可以獨立運轉的最早時刻(將總是能夠在 C-new 配置下選出新的領導人)。在此之前,可能只能從 C-old 中選出領導人。

第三個問題是,那些被移除的服務器(不在 C-new 中)可能會擾亂集羣。這些服務器將不會再接收到心跳,所以當選舉超時,它們就會進行新的選舉過程。它們會發送帶有新任期號的 RequestVote RPCs ,這樣會導致當前的 leader 回到 follower 狀態。新的 leader 最終會被選出來,但是被移除的服務器將會再次超時,然後這個過程會再次重複,導致系統可用性很差。

爲了防止這種問題,當服務器認爲當前 leader 存在時,服務器會忽略RequestVote RPCs 。特別的,當服務器在最小選舉超時時間內收到一個 RequestVote RPC,它不會更新任期號或者投票。這不會影響正常的選舉,每個服務器在開始一次選舉之前,至少等待最小選舉超時時間。相反,這有利於避免被移除的服務器的擾亂:如果 leader 能夠發送心跳給集羣,那它就不會被更大的任期號廢黜。

7 日誌壓縮

Raft 的日誌在正常操作中隨着包含更多的客戶端請求不斷地增長,但是在實際的系統中,日誌不能無限制地增長。隨着日誌越來越長,它會佔用越來越多的空間,並且需要花更多的時間來回放。如果沒有一定的機制來清除日誌中積累的過期的信息,最終就會帶來可用性問題。

快照技術是日誌壓縮最簡單的方法。在快照技術中,整個當前系統的狀態都以快照的形式持久化到穩定的存儲中,該時間點之前的日誌全部丟棄。快照技術被使用在 Chubby 和 ZooKeeper 中,接下來的章節會介紹 Raft 中的快照技術。

增量壓縮方法,例如日誌清理或者日誌結構合併樹(log-structured merge trees,LSM 樹),都是可行的。這些方法每次只對一小部分數據進行操作,這樣就分散了壓縮的負載壓力。首先,它們先選擇一個積累了大量已經被刪除或者被覆蓋的對象的數據區域,然後重寫該區域還活着的對象,之後釋放該區域。和快照技術相比,它們需要大量額外的機制和複雜性,快照技術通過操作整個數據集來簡化該問題。狀態機可以用和快照技術相同的接口來實現 LSM 樹,但是日誌清除方法就需要修改 Raft 了。

圖12

一臺服務器用一個新快照替代了它日誌中已經提交了的條目(索引 1 到 5),該快照只存儲了當前的狀態(變量 x 和 y 的值)。快照的 last included index 和 last included term 被保存來定位日誌中條目 6 之前的快照

圖 12 展示了 Raft 中快照的基本思想。每個服務器獨立地創建快照,快照只包括自己日誌中已經被提交的條目。主要的工作是狀態機將自己的狀態寫入快照中。Raft 快照中也包含了少量的元數據:the last included index 指的是最後一個被快照取代的日誌條目的索引值(狀態機最後應用的日誌條目),the last included term 是該條目的任期號。保留這些元數據是爲了支持快照後第一個條目的 AppendEntries 一致性檢查,因爲該條目需要之前的索引值和任期號。爲了支持集羣成員變更(第 6 節),快照中也包括日誌中最新的配置作爲 last included index 。一旦服務器完成寫快照,他就可以刪除 last included index 之前的所有日誌條目,包括之前的快照。

儘管通常服務器都是獨立地創建快照,但是 leader 必須偶爾發送快照給一些落後的跟隨者。這通常發生在 leader 已經丟棄了需要發送給 follower 的下一條日誌條目的時候。幸運的是這種情況在常規操作中是不可能的:一個與 leader 保持同步的 follower 通常都會有該日誌條目。然而一個例外的運行緩慢的 follower 或者新加入集羣的服務器(第 6 節)將不會有這個條目。這時讓該 follower 更新到最新的狀態的方式就是通過網絡把快照發送給它。

Leader 使用 InstallSnapshot RPC 來發送快照給太落後的 follower ;見圖 13。當 follower 收到帶有這種 RPC 的快照時,它必須決定如何處理已經存在的日誌條目。通常該快照會包含接收者日誌中沒有的信息。在這種情況下,follower 丟棄它所有的日誌;這些會被該快照所取代,並且可能一些沒有提交的條目會和該快照產生衝突。如果接收到的快照是自己日誌的前面部分(由於網絡重傳或者錯誤),那麼被快照包含的條目將會被全部刪除,但是快照之後的條目仍然有用並保留。

圖13

這種快照的方式違反了 Raft 的 strong leader 原則,因爲 follower 可以在不知道 leader 狀態的情況下創建快照。但是我們認爲這種違背是合乎情理的。Leader 的存在,是爲了防止在達成一致性的時候的衝突,但是在創建快照的時候,一致性已經達成,因此沒有決策會衝突。數據依然只能從 leader 流到 follower ,只是 follower 可以重新組織它們的數據了。

我們考慮過一種可替代的基於 leader 的快照方案,在該方案中,只有leader 會創建快照,然後 leader 會發送它的快照給所有的 follower 。但是這樣做有兩個缺點。第一,發送快照會浪費網絡帶寬並且延緩了快照過程。每個 follower 都已經擁有了創建自己的快照所需要的信息,而且很顯然,follower 從本地的狀態中創建快照遠比通過網絡接收別人發來的要來得經濟。第二,leader 的實現會更加複雜。例如,leader 發送快照給 follower 的同時也要並行地將新的日誌條目發送給它們,這樣纔不會阻塞新的客戶端請求。

還有兩個問題會影響快照的性能。首先,服務器必須決定什麼時候創建快照。如果快照創建過於頻繁,那麼就會浪費大量的磁盤帶寬和其他資源;如果創建快照頻率太低,就要承擔耗盡存儲容量的風險,同時也增加了重啓時日誌回放的時間。一個簡單的策略就是當日志大小達到一個固定大小的時候就創建一次快照。如果這個閾值設置得顯著大於期望的快照的大小,那麼快照的磁盤帶寬負載就會很小。

第二個性能問題就是寫入快照需要花費一段時間,並且我們不希望它影響到正常的操作。解決方案是通過寫時複製的技術,這樣新的更新就可以在不影響正在寫的快照的情況下被接收。例如,具有泛函數據結構的狀態機天然支持這樣的功能。另外,操作系統對寫時複製技術的支持(如 Linux 上的 fork)可以被用來創建整個狀態機的內存快照(我們的實現用的就是這種方法)。

8 客戶端交互

本節介紹客戶端如何和 Raft 進行交互,包括客戶端如何找到 leader 和 Raft 是如何支持線性化語義的。這些問題對於所有基於一致性的系統都存在,並且 Raft 的解決方案和其他的也差不多。

Raft 的客戶端發送所有的請求給 leader 。當客戶端第一次啓動的時候,它會隨機挑選一個服務器進行通信。如果客戶端第一次挑選的服務器不是 leader ,那麼該服務器會拒絕客戶端的請求並且提供關於它最近接收到的領導人的信息(AppendEntries 請求包含了 leader 的網絡地址)。如果 leader 已經崩潰了,客戶端請求就會超時;客戶端之後會再次隨機挑選服務器進行重試。

我們 Raft 的目標是要實現線性化語義(每一次操作立即執行,只執行一次,在它的調用和回覆之間)。但是,如上述,Raft 可能執行同一條命令多次:例如,如果 leader 在提交了該日誌條目之後,響應客戶端之前崩潰了,那麼客戶端會和新的 leader 重試這條指令,導致這條命令被再次執行。解決方案就是客戶端對於每一條指令都賦予一個唯一的序列號。然後,狀態機跟蹤每個客戶端已經處理的最新的序列號以及相關聯的回覆。如果接收到一條指令,該指令的序列號已經被執行過了,就立即返回結果,而不重新執行該請求。

只讀的操作可以直接處理而不需要記錄日誌。但是,如果不採取任何其他措施,這麼做可能會有返回過時數據(stale data)的風險,因爲 leader 響應客戶端請求時可能已經被新的 leader 替代了,但是它還不知道自己已經不是最新的 leader 了。線性化的讀操作肯定不會返回過時數據,Raft 需要使用兩個額外的預防措施來在不使用日誌的情況下保證這一點。首先,leader 必須有關於哪些日誌條目被提交了的最新信息。Leader 完整性特性保證了 leader 一定擁有所有已經被提交的日誌條目,但是在它任期開始的時候,它可能不知道哪些是已經被提交的。爲了知道這些信息,它需要在它的任期裏提交一個日誌條目。Raft 通過讓 leader 在任期開始的時候提交一個空的沒有任何操作的日誌條目到日誌中來處理該問題。第二,leader 在處理只讀請求之前必須檢查自己是否已經被替代了(如果一個更新的 leader 被選舉出來了,它的信息就是過時的了)。Raft 通過讓 leader 在響應只讀請求之前,先和集羣中的過半節點交換一次心跳信息來處理該問題。另一種可選的方案,leader 可以依賴心跳機制來實現一種租約的形式,但是這種方法依賴 timing 來保證安全性(假設時間誤差是有界的)。

Raft 網站

參考資料

  • [1] BOLOSKY, W. J., BRADSHAW, D., HAAGENS, R. B., KUSTERS, N. P., AND LI, P. Paxos replicated state machines as the basis of a high-performance data store. In Proc. NSDI’11, USENIX Conference on Networked Systems Design and Implementation (2011), USENIX, pp. 141–154.

  • [2] BURROWS, M. The Chubby lock service for loosely- coupled distributed systems. In Proc. OSDI’06, Sympo- sium on Operating Systems Design and Implementation (2006), USENIX, pp. 335–350.

  • [3] CAMARGOS, L. J., SCHMIDT, R. M., AND PEDONE, F. Multicoordinated Paxos. In Proc. PODC’07, ACM Sym- posium on Principles of Distributed Computing (2007), ACM, pp. 316–317.

  • [4] CHANDRA, T. D., GRIESEMER, R., AND REDSTONE, J. Paxos made live: an engineering perspective. In Proc. PODC’07, ACM Symposium on Principles of Distributed Computing (2007), ACM, pp. 398–407.

  • [5] CHANG, F., DEAN, J., GHEMAWAT, S., HSIEH, W. C., WALLACH, D. A., BURROWS, M., CHANDRA, T., FIKES, A., AND GRUBER, R. E. Bigtable: a distributed storage system for structured data. In Proc. OSDI’06, USENIX Symposium on Operating Systems Design and Implementation (2006), USENIX, pp. 205–218.

  • [6] CORBETT, J. C., DEAN, J., EPSTEIN, M., FIKES, A., FROST, C., FURMAN, J. J., GHEMAWAT, S., GUBAREV, A., HEISER, C., HOCHSCHILD, P., HSIEH, W., KAN- THAK, S., KOGAN, E., LI, H., LLOYD, A., MELNIK, S., MWAURA, D., NAGLE, D., QUINLAN, S., RAO, R., ROLIG, L., SAITO, Y., SZYMANIAK, M., TAYLOR, C., WANG, R., AND WOODFORD, D. Spanner: Google’s globally-distributed database. In Proc. OSDI’12, USENIX Conference on Operating Systems Design and Implemen- tation (2012), USENIX, pp. 251–264.

  • [7] COUSINEAU, D., DOLIGEZ, D., LAMPORT, L., MERZ, S., RICKETTS, D., AND VANZETTO, H. TLA+ proofs. In Proc. FM’12, Symposium on Formal Methods (2012), D. Giannakopoulou and D. Me ry, Eds., vol. 7436 of Lec- ture Notes in Computer Science, Springer, pp. 147–154.

  • [8] GHEMAWAT, S., GOBIOFF, H., AND LEUNG, S.-T. The Google file system. In Proc. SOSP’03, ACM Symposium on Operating Systems Principles (2003), ACM, pp. 29–43.

  • [9] GRAY,C.,ANDCHERITON,D.Leases:Anefficientfault- tolerant mechanism for distributed file cache consistency. In Proceedings of the 12th ACM Ssymposium on Operating Systems Principles (1989), pp. 202–210.

  • [10] HERLIHY, M. P., AND WING, J. M. Linearizability: a correctness condition for concurrent objects. ACM Trans- actions on Programming Languages and Systems 12 (July 1990), 463–492.

  • [11] HUNT, P., KONAR, M., JUNQUEIRA, F. P., AND REED, B. ZooKeeper: wait-free coordination for internet-scale systems. In Proc ATC’10, USENIX Annual Technical Con- ference (2010), USENIX, pp. 145–158.

  • [12] JUNQUEIRA, F. P., REED, B. C., AND SERAFINI, M. Zab: High-performance broadcast for primary-backup sys- tems. In Proc. DSN’11, IEEE/IFIP Int’l Conf. on Depend- able Systems & Networks (2011), IEEE Computer Society, pp. 245–256.

  • [13] KIRSCH, J., AND AMIR, Y. Paxos for system builders. Tech. Rep. CNDS-2008-2, Johns Hopkins University, 2008.

  • [14] LAMPORT, L. Time, clocks, and the ordering of events in a distributed system. Commununications of the ACM 21, 7 (July 1978), 558–565.

  • [15] LAMPORT, L. The part-time parliament. ACM Transac- tions on Computer Systems 16, 2 (May 1998), 133–169.

  • [16] LAMPORT, L. Paxos made simple. ACM SIGACT News 32, 4 (Dec. 2001), 18–25.

  • [17] LAMPORT, L. Specifying Systems, The TLA+ Language and Tools for Hardware and Software Engineers. Addison- Wesley, 2002.

  • [18] LAMPORT, L. Generalized consensus and Paxos. Tech. Rep. MSR-TR-2005-33, Microsoft Research, 2005.

  • [19] LAMPORT, L. Fast paxos. Distributed Computing 19, 2 (2006), 79–103.

  • [20] LAMPSON, B. W. How to build a highly available system using consensus. In Distributed Algorithms, O. Baboaglu and K. Marzullo, Eds. Springer-Verlag, 1996, pp. 1–17.

  • [21] LAMPSON, B. W. The ABCD’s of Paxos. In Proc. PODC’01, ACM Symposium on Principles of Distributed Computing (2001), ACM, pp. 13–13.

  • [22] LISKOV, B., AND COWLING, J. Viewstamped replica- tion revisited. Tech. Rep. MIT-CSAIL-TR-2012-021, MIT, July 2012.
    17

  • [23] LogCabin source code. logcabin/logcabin.
    http://github.com/

  • [24] LORCH, J. R., ADYA, A., BOLOSKY, W. J., CHAIKEN, R., DOUCEUR, J. R., AND HOWELL, J. The SMART way to migrate replicated stateful services. In Proc. Eu- roSys’06, ACM SIGOPS/EuroSys European Conference on Computer Systems (2006), ACM, pp. 103–115.

  • [25] MAO, Y., JUNQUEIRA, F. P., AND MARZULLO, K. Mencius: building efficient replicated state machines for
    WANs. In Proc. OSDI’08, USENIX Conference on Operating Systems Design and Implementation (2008), USENIX, pp. 369–384.

  • [26] MAZIE` RES, D. Paxos made practical.
    //www.scs.stanford.edu/ dm/home/ papers/paxos.pdf, Jan. 2007.

  • [27] MORARU, I., ANDERSEN, D. G., AND KAMINSKY, M. There is more consensus in egalitarian parliaments. In Proc. SOSP’13, ACM Symposium on Operating System Principles (2013), ACM.

  • [28] Raft user study. http://ramcloud.stanford. edu/ ongaro/userstudy/.

  • [29] OKI, B. M., AND LISKOV, B. H. Viewstamped replication: A new primary copy method to support highly-available distributed systems. In Proc. PODC’88, ACM Symposium on Principles of Distributed Computing (1988), ACM, pp. 8–17.

  • [30] O’NEIL, P., CHENG, E., GAWLICK, D., AND ONEIL, E. The log-structured merge-tree (LSM-tree). Acta Informat- ica 33, 4 (1996), 351–385.

  • [31] ONGARO, D. Consensus: Bridging Theory and Practice. PhD thesis, Stanford University, 2014 (work in progress).http://ramcloud.stanford.edu/ ongaro/ thesis.pdf.

  • [32] ONGARO, D., AND OUSTERHOUT, J. In search of an understandable consensus algorithm. In Proc ATC’14, USENIX Annual Technical Conference (2014), USENIX.

  • [33] OUSTERHOUT, J., AGRAWAL, P., ERICKSON, D., KOZYRAKIS, C., LEVERICH, J., MAZIE`RES, D., MI- TRA, S., NARAYANAN, A., ONGARO, D., PARULKAR, G., ROSENBLUM, M., RUMBLE, S. M., STRATMANN, E., AND STUTSMAN, R. The case for RAMCloud. Com- munications of the ACM 54 (July 2011), 121–130.

  • [34] Raft consensus algorithm website. http://raftconsensus.github.io.

  • [35] REED, B. Personal communications, May 17, 2013.

  • [36] ROSENBLUM, M., AND OUSTERHOUT, J. K. The design and implementation of a log-structured file system. ACM Trans. Comput. Syst. 10 (February 1992), 26–52.

  • [37] SCHNEIDER, F. B. Implementing fault-tolerant services using the state machine approach: a tutorial. ACM Com- puting Surveys 22, 4 (Dec. 1990), 299–319.

  • [38] SHVACHKO, K., KUANG, H., RADIA, S., AND CHANSLER, R. The Hadoop distributed file system. In Proc. MSST’10, Symposium on Mass Storage Sys- tems and Technologies (2010), IEEE Computer Society, pp. 1–10.

  • [39] VAN RENESSE, R. Paxos made moderately complex. Tech. rep., Cornell University, 2012.

原文出處:

http://linbingdong.com/2017/02/19/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%80%E8%87%B4%E6%80%A7%E7%AE%97%E6%B3%95%EF%BC%9ARaft%20%E7%AE%97%E6%B3%95%EF%BC%88Raft%20%E8%AE%BA%E6%96%87%E7%BF%BB%E8%AF%91%EF%BC%89/



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