Raft算法之成員變更

成員變更是跟leader選舉、日誌同步、安全、日誌壓縮一樣,都是Raft算法的核心概念。但成員變更是最難理解的。所以單列一篇總結。

將成員變更納入到算法中是Raft易於應用到實踐中的關鍵,相對於Paxos,它給出了明確的變更過程(實踐的基礎,任何現實的系統中都會遇到因爲硬件故障等原因引起的節點變更的操作)。

顯然,我們可以通過shutdown集羣,然後變更配置後重啓集羣的方式達到成員變更的目的。但是這種操作會損失系統的可用性,同時會帶來操作失誤引起的風險。支持自動化配置,即配置可以在集羣運行期間進行動態的變更(不影響可用性)顯然是一個非常重要的特性。

常見問題

一、常規處理成員變更有什麼問題?

會出現雙Leader問題。

成員變更是在集羣運行過程中副本發生變化,如增加/減少副本數、節點替換等。

成員變更也是一個分佈式一致性問題,既所有服務器對新成員達成一致。但是成員變更又有其特殊性,因爲在成員變更的一致性達成的過程中,參與投票的進程會發生變化。

如果將成員變更當成一般的一致性問題,直接向Leader發送成員變更請求,Leader複製成員變更日誌,達成多數派之後提交,各服務器提交成員變更日誌後從舊成員配置(Cold)切換到新成員配置(Cnew)。

因爲各個服務器提交成員變更日誌的時刻可能不同,造成各個服務器從舊成員配置(Cold)切換到新成員配置(Cnew)的時刻不同。

成員變更不能影響服務的可用性,但是成員變更過程的某一時刻,可能出現在Cold和Cnew中同時存在兩個不相交的多數派,進而可能選出兩個Leader,形成不同的決議,破壞安全性。

成員變更的某一時刻Cold和Cnew中同時存在兩個不相交的多數派

上圖中Server1可以通過自身和Server2的選票成爲Leader(滿足舊配置下收到大多數選票的原則);Server3可以通過自身和Server4、Server5的選票成爲Leader(滿足新配置下,即集羣有5個節點的情況下的收到大多數選票的原則);此時整個集羣可能在同一任期中出現了兩個Leader,這和協議是違背的。

二、有什麼解決方案?

兩種方案,

  • 方案一、一階段成員變更;
  • 方案二、二階段成員變更;

方案一、一階段成員變更

如果增強成員變更的限制,假設Cold與Cnew任意的多數派交集不爲空,這兩個成員配置就無法各自形成多數派,那麼成員變更方案就可能簡化爲一階段。

那麼如何限制Cold與Cnew,使之任意的多數派交集不爲空呢?方法就是每次成員變更只允許增加或刪除一個成員。

可從數學上嚴格證明,只要每次只允許增加或刪除一個成員,Cold與Cnew不可能形成兩個不相交的多數派。

一階段成員變更:

  • 成員變更限制每次只能增加或刪除一個成員(如果要變更多個成員,連續變更多次)。
  • 成員變更由Leader發起,Cnew得到多數派確認後,返回客戶端成員變更成功。

一次成員變更成功前不允許開始下一次成員變更,因此新任Leader在開始提供服務前要將自己本地保存的最新成員配置重新投票形成多數派確認。

Leader只要開始同步新成員配置,即可開始使用新的成員配置進行日誌同步。

方案二、二階段成員變更

爲了保證安全性,Raft採用了一種兩階段的方式。

集羣先從舊成員配置Cold切換到一個稱爲多邊共識的中間階段(joint consensus)joint consensus 是舊成員配置Cold和新成員配置Cnew的組合 Cold U Cnew,也即Cold+new,一旦 joint consensus Cold+new被提交,系統再切換到新成員配置Cnew

joint consensus 狀態下:

  • 日誌被提交給新舊配置下所有的節點;
  • 新舊配置中所有機器都可能成爲Leader;
  • 達成一致(選舉和提交)要在兩種配置上獲得超過半數的支持;

Raft兩階段成員變更過程如下:

Leader收到從Cold切換成Cnew的成員變更請求;Leader做了兩步走的操作:

第一階段

Leader在本地生成一個新的 log entry,其內容是Cold+new,代表當前時刻新舊成員配置共存,寫入本地日誌,同時將該 log entry 複製至Cold+new中的所有副本。在此之後新的日誌同步需要保證得到Cold和Cnew兩個多數派的確認

Follower收到Cold+newlog entry 後更新本地日誌,並且此時就以該配置作爲自己的成員配置;

如果Cold和Cnew中的兩個多數派確認了Cold+new這條日誌,Leader就提交這條 log entry

配置變更唯一的不同在於它們會立即生效,一旦服務器將新配置記錄到日誌中,那麼它就立刻生效,並不需要等待該日誌記錄變爲已提交狀態。所以此時在領導者上已經認爲 Cold+new 已生效,這意味着對於要提交的任何日誌條目,要求該條目分別在新舊配置服務器下同時都成爲大多數。

第二階段

接下來Leader生成一條新的 log entry,其內容是新成員配置Cnew,同樣將該 log entry 寫入本地日誌,同時複製到Follower上;

Follower收到新成員配置Cnew後,將其寫入日誌,並且從此刻起,就以該配置作爲自己的成員配置,並且如果發現自己不在Cnew這個成員配置中會自動退出;

Leader收到Cnew的多數派確認後,表示成員變更成功,後續的日誌只要得到Cnew多數派確認即可。Leader給客戶端回覆成員變更執行成功。

-w585

Review成員變更

一、如果當前的Leader不在Cnew的配置中會怎麼樣(即當前的Leader是一個要被下線的節點)?

在Cold+new的狀態下,Leader依舊可用;在Cnew被提交之後Leader實際已經從集羣中脫離,此時可以對Leader節點進行下線操作,而新集羣則會在Cnew的配置下重新選舉出一個Leader。

二、如果在配置分發過程中Leader Crash了會怎麼樣?

這個問題要分爲多種情況:

  1. Cnew已經分發到超過半數節點;
  2. Cnew還沒分發到超過半數的節點;

情況1:Cnew已經分發到超過半數節點

集羣開始重新選舉,此時在Cnew的規則下,舊節點(不存在新配置中的節點)不會贏得選舉(因爲他們要在Cold+new的情況下決定,但是拿不到Cnew的選票),只有拿到Cnew的節點可能成爲Leader並繼續下發Cnew配置,流程恢復。

情況2:Cnew還沒分發到超過半數的節點

這種情況下,Cold+new和Cnew的節點都可以成爲Leader,但是無所謂,因爲無論誰成爲Leader,都能根據當前的配置繼續完成後續流程(如果是Cnew那麼相當與完成了最終的配置,不在Cnew的節點會因爲沒有心跳數據而失效)

三、舊節點下線造成的問題:舊節點收不到心跳觸發選舉,發送請求給Cold+new中的節點,是否會影響集羣正常運行

Raft的處理方式:當節點確信有Leader存在時,不會進行投票(在Leader超時之前收到新的投票請求時不會提升term和投票)。且開始選舉之前等待一個選舉超時時間,這樣在新Leader正常工作的情況下,不會受到舊節點的影響。

四、新的服務器沒有任何數據,加入進來怎麼保證系統的可用性(這個時候新日誌沒辦法提交就沒辦法響應給客戶端)?

新加入的節點需要時間複製數據,在這個過程完成之前,Raft採用以下機制來保證可用性:新加入節點沒有投票權(Leader複製日誌給他們,但是不將他們考慮在機器數量裏面——即在判斷是否超過半數時不把這些節點考慮在內),直到這些節點的日誌追上其他節點。

結束

兩階段成員變更比較通用且容易理解,但是實現比較複雜,同時兩階段的變更協議也會在一定程度上影響變更過程中的服務可用性,因此我們期望增強成員變更的限制,以簡化操作流程。

兩階段成員變更,之所以分爲兩個階段,是因爲對Cold與Cnew的關係沒有做任何假設,爲了避免Cold和Cnew各自形成不相交的多數派選出兩個Leader,才引入了兩階段方案。

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