前言
Raft
算法爲主從結構, 其分佈式一致性來源於集羣的寫全委託給Leader
,Leader
進程自身保證順序與一致性, 併發起投票要求Follower
追加寫, 一旦過半贊成寫請求(同時附加寫的動作), 則該寫完成。
.
需要注意的是, 集羣的寫是線性一致性/強一致性的, 同時基於Follower
轉發的集羣的讀也是現行一致性/強一致性的。並非ZAB的順序一致性。
這兩天想到一個問題, raft算法的成員共識性問題。我之前的理解是:
Raft有共識性問題。 當未實現所有日誌提交的Follower,之後被選舉爲新的leader之後, 源於raft日誌的leader覆蓋規則, 將導致數據丟失。
↑↑上面這個想法是錯誤的↑↑, Raft
算法的安全性保證(阻止不包含最新Term
和日誌編號的Follower
成爲Leader
、複製舊Leader
數據)達成共識,確保了成員數據的一致性(相應的,ZAB使用主從相互拷貝的形式,達成集羣共識)。
關於
Raft
, 我對其做了一個Java
版的實現, 地址在: https://github.com/srctar/raft。 歡迎閱讀。
目前實現了 raft 協議的下述功能:
- 集羣選舉.
- 數據一致性.
- 集羣配置(集羣節點信息, 以及集羣的數目)更改.
- 緊急提交.
- 日誌壓縮.
Raft算法選舉
選舉流程參考網站:http://thesecretlivesofdata.com/raft/
本文的部分圖片以及理論基礎參考: https://blog.csdn.net/luoyhang003/article/details/61915666
如下Gif:
選舉主要注意兩點:
- 心跳超時(不管是初始態還是運行態);
- 只要當前機器尚未投票(包括自己), 就一定投票給申請投票者,同時重置心跳準備再次超時。
由於是多線程操作, 時序圖與流程圖皆不好畫, 請參閱文字:
Raft
爲集羣狀態定義爲:ELECTION
(選舉態),PROCESSING
(運行態)
.
每個Raft
節點有三個狀態:FOLLOWER
、LEADER
、CANDIDATE
(選舉者)
- 獨立線程心跳超時(一般是100ms);
a. 獨立線程可以位於FOLLOWER
, 也可以位於剛啓動的集羣節點,還可以位於宕機、網絡中斷的節點。
b.LEADER
節點無該獨立線程(它負責給別的節點發心跳)。
c. - - 該線程休眠 150~300ms,高於心跳超時時間100ms。
a.此操作非常重要,用以防止選票分散,進而導致長期超時
b.休眠中的線程可以接受投票。
c. - - 投自己一票,並向集羣中的節點申請投票。
a. 節點處於集羣選舉中ELECTION
,且未給任何節點投票,節點默認接受投票申請。
b. - - 當某節點收到過半讚許,節點立馬轉化爲
LEADER
。 關閉心跳線程, 同時給其它節點發送心跳。
a. 節點處於運行態PROCESSING
, 且自身是LEADER
,顯然,一定有個老leader網斷了,然後又網好了。 只要對方的Term
大於自己, 那自己立馬轉化爲FOLLOWER
。
b. - - 選舉態的節點收到心跳, 轉化爲
FOLLOWER
, 同時重設心跳線程。
a. 注意, 節點處於選舉態ELECTION
, 需要心跳發送者的Term
高於自己,否則返回拒絕(APPEND_ENTRIES_DENY
)
b. 節點處於運行態PROCESSING
, 需要匹配心跳者是否是集羣LEADER
。是則重置心跳線程, 否則直接拒絕。
c. -
如上選舉算法設計, 合理的確保下如下表格中的case, 能快速選上leader、且非過半宕機時,集羣可用:
Case | 擔憂點 | 解決方案 |
---|---|---|
正常啓動選Leader | 選不上 | 重選/啓動時參與選舉有線程休眠時間150~300ms, 此時接受投票一定贊同,確保選舉 |
不過半的Follower 宕機/失聯 |
可用性 | 可用, Leader 不宕機,不影響選舉, 不過半宕機,不影響數據投票 |
過半Follower宕機 | 可用性 | Leader 不宕機,還能接受數據, 過半宕機,影響數據投票, 集羣不可用 |
不過半宕機, 包含Leader |
可用性 | 可用, 心跳超時再選舉, 通常一輪就能選出新Leader ,集羣正常服務 |
不過半宕機, 包含Leader |
可用性 | 不可用, 選不出Leader |
Leader 失聯 |
集羣狀態 | 其他機器依舊能選上新Leader , 期序Term 累加,舊Leader 恢復之後接受心跳轉爲Follower |
Leader 宕機 |
集羣狀態 | 其他機器依舊能選上新Leader , 重啓之後接受心跳轉爲Follower |
Raft算法數據一致性
此處講解的一致性, 分爲兩點:
- Raft 集羣正常運行態的數據一致性;
- Raft 集羣宕機恢復後的數據一致性。
正常運行態的強一致性
正常運行態, Raft的寫滿足強一致性/線性一致性。
但是讀只能滿足到順序一致性。
也是如上圖, 在最後一步, 如果客戶端讀到還沒有全部同步完畢的節點, 可能導致讀取到老舊的數據。
但是源於Raft
的日誌式數據屬性, 客戶端不再會讀到更早的數據, 只會讀取到更新的數據, 因此滿足順序一致性。
Raft集羣的數據共識(集羣恢復時)
本節講的是集羣某些機器宕機, 數據也沒有來得及同步的情況下, 集羣如何保證的數據一致性。
↓↓↓↓↓↓↓↓注意下面例子,Raft
是解決了的, 實際並不存在↓↓↓↓↓↓↓↓
極端情況,新Leader上位,集羣狀態卻如下圖:
如上圖,相對於新的leader:
- a、b 丟失了部分數據
- c、d 多出來部分數據
- e、f 既丟失, 又多出來了數據
畢竟Raft
的同步邏輯,過半提交則爲提交, 之後Leader
一直重試保證數據一致。
像b, 網絡不佳,就是沒同步上;
像d, 它就是上一個leader,自身提交前,還沒來得及集羣同步,網斷了;
那類似這種情況, Raft
集羣的數據共識是怎麼達成的呢? 如下方案:
- 選舉限制。 除了匹配
Term
外, 還匹配日誌版本, 大於等於自身日誌版本的投票纔會被授予。以此確保多數贊成的數據是最新的。 - 嘗試複製老舊
Leader
上已提交(過半贊成)的日誌,因爲已提交, 則至少當前被複制條目以及之前的日誌都是可以被提交的。
如上操作確保了只有持有最新日誌的節點才能成爲
Leader
, 且它將會嘗試複製老舊Leader
的日誌數據。
此項操作通過排序和重發布確保數據的有序性, 但是會一定程度的增加系統複雜度。
Raft算法的其他集羣變更、日誌壓縮功能
這部分功能沒有實現, 具體可參閱 https://blog.csdn.net/luoyhang003/article/details/61915666