本文是Raft算法論文的學習筆記,Raft是一個用於管理多副本日誌的共識算法。共識算法運行集羣即便在少數節點崩潰的狀況下,讓集羣中的節點一致工做。Raft算法有如下特性:git
共識算法涉及到了多副本狀態機的概念,多副本狀態機就是一組服務器作相同的狀態副本,即便在一些服務器下線以後仍是能夠繼續操做。多副本狀態機一般使用多副本日誌實現,而維護多副本日誌就是共識算法的任務。github
共識算法有如下特性:算法
Paxos首次解決了一致性協議問題,可是問題在於:安全
Raft主要使用了兩個思路提升可理解性:服務器
Raft完整算法以下圖所示:性能
Raft算法能夠分爲三個子問題進行討論:學習
index
和term
,那麼到這條爲止的日誌條目都是相同的;index
的其餘不一樣命令。任何服務器會處於三種狀態的一種:領導、下屬和候選,狀態的切換狀態機以下所示:設計
Raft將時間劃分爲任意長度的term,每一個term從一次選舉開始,贏得選舉的節點做爲當前term的領導。當選舉失敗或者領導節點崩潰,那麼須要開始一個新的term。3d
領導週期性向下屬發送AppendEntries RPC來保持「心跳」,若是沒有新增的日誌就發送空內容,當下屬在一段時間(選舉超時)內沒有收到任何消息時,那麼進入候選狀態啓動選舉。若是候選人獲得多數服務器的投票,服務器轉換爲領導狀態,向其餘服務器發送AppendEntries RPC。日誌
爲了不同時產生多個候選節點,每一個服務器的選舉超時時間都須要從一個區間中隨機選擇。在集羣沒有領導的時候,候選節點須要等待一段選舉超時時間後再開始下一輪選舉。
領導節點從客戶端接收條目後追加到本身的日誌中,而後經過AppendEntries RPC將日誌發送給其餘節點創建副本。當領導得知副本到達多數節點後,便可提交日誌條目。Raft保證已經提交的條目是持久的,而且最終會被所有可用的狀態機執行。當日志條目備份到了多數的服務器上,那麼便可提交:
Raft知足如下特徵來保證日誌匹配性:
term
和index
,那麼他們保存同一條命令;term
和index
,那麼他們以前的全部日誌都是相同的。當領導節點開始管理集羣的時候,各個服務器上的日誌可能以下:
有些下屬丟失了日誌條目(a-b),有些下屬包含了額外可是還沒有提交的條目(c-d),或者兩種狀況都有(e-f)。其中f是由於它是term
2和3的領導,可是都沒有提交成功任何條目。不管如何,下屬服務器上的日誌中衝突的條目會被領導的日誌所覆蓋。
領導服務器維護了nextIndex
來保存須要發送給下屬的下一個條目,當領導初始化的時候,將nextIndex
初始化爲服務器的最後一個條目。若是下屬服務器的日誌和領導服務器日誌不同,那麼AppendEntries RPC會失敗,這個時候領導將nextIndex
減一以後重試。對於那些由於故障致使日誌丟失或者落後的服務器,AppendEntries的一致性檢查可以幫助恢復日誌。
領導完整性要求任意一個term
的領導必須包含以前term
提交的全部日誌條目。爲了保證這個特徵,Raft要求候選服務器必須包含全部的提交日誌才能得到選舉。所以,RequestVote RPC包含了候選服務器的日誌,若是投票服務器的日誌更新,那麼拒絕投票。
Raft永遠不會提交以前term
的條目。以下圖,假設領導節點S1將2備份到S2以後掉線(a),而後S5成爲領導收到3後掉線(b),而後S1從新成爲領導後繼續備份2到大多數節點後提交2,可是再次下線(c),可是若是S5從新成爲領導以後會覆蓋2(d),可是若是4被提交的話,2就不會被覆蓋,由於S5不可能成爲領導。
Raft領導只會提交當前term
以內的條目;這樣一來,當一個條目提交以後,可以確保以前全部的條目都被提交,知足日誌匹配性。
咱們可使用反證法證實領導完整性成立。假設在term
提交的一條日誌條目沒有保存到將來的領導服務器日誌中。那麼令不包含該條條目的最小term
爲U()。
領導完整性成立進一步能夠證實狀態機安全性。
下屬和候選服務器崩潰以後,RPC請求就會失敗,Raft採用無限次嘗試的方法,當服務器重啓以後隨着RPC處理的進行天然恢復。
Raft的安全性並不依賴於時間:系統不會由於某些事件發生地更快或者更慢而產生不正確地結果。
可是,Raft對於選舉超時時間仍是有着必定地要求:
broadcastTime是服務器並行向全部其餘服務器發送RPC須要地時間,MTBF是單個服務器發送故障的平均時間。
爲了保障安全性,配置更改須要使用兩階段的形式。在Raft中應用配置更改的時候,首先進入一個稱爲聯合共識的過渡狀態,在這個狀態中
集羣配置被保存在一個特殊的日誌條目中,一旦一個服務器將新配置條目加入日誌,它就會在將來全部操做中使用新的配置。
配置變動過程以下:
在變動配置的時候會存在如下三個問題:
根據Raft算法,重啓的節點能夠重放日誌進行恢復,可是若是日誌很長會給恢復過程帶來壓力。一個直觀的方法就是經過快照來壓縮日誌。
每一個節點獨立維護本身的快照,只將已經提交的日誌保存到快照中。節點須要保存快照包含的最後一個term
和index
用於處理AppendEntries是檢查一致性。對於領導節點,須要發送InstallSnapshot遠程調用將本身的快照給那些落後的節點幫助其遇上最新狀態。
快照機制存在兩個性能問題:
客戶端須要找到正確的領導節點,客戶端會隨機鏈接一個節點,隨機節點會告知客戶端領導節點的信息。
若是領導節點在提交某日誌條目後沒來得及通知客戶端時崩潰,那麼客戶端聯繫新領導節點重試的時候重複執行指令,可讓客戶端給每個指令設置一個序列號解決。
因爲只讀操做不須要提交日誌條目,所以可能會從舊的領導節點讀取舊數據,Raft使用了兩個額外要求來解決: