一致性协议raft详解(二):安全性
一致性協議raft詳解(二):安全性
- 前言
- 安全性
- log recovery
- 為什么no-op能解決不一致的問題?
- 成員變更
- Single mempership change
- raft用到的隨機時間
- raft 腦裂
- 數據到達 Leader 節點前
- 數據到達 Leader 節點,但未復制到 Follower 節點
- 數據到達 Leader 節點,成功復制到 Follower 所有節點,但還未向 Leader 響應接收
- 數據到達 Leader 節點,成功復制到 Follower 部分節點,但還未向 Leader 響應接收
前言
有關一致性協議的資料網上有很多,當然錯誤也有很多。筆者在學習的過程中走了不少彎路。現在回過頭來看,最好的學習資料就是Leslie Lamport和Diego Ongaro的數篇論文、Ongaro在youtube上發的三個視頻講解,以及何登成的ppt。
本系列文章是只是筆者在學習一致性協議過程中的摘抄和總結,有疏漏之處敬請諒解,歡迎討論。
安全性
raft的log replication機制并不能充分的保證每一個狀態機會按照相同的順序執行相同的指令。例如,一個follower可能會進入不可用狀態同時leader已經提交了若干的日志條目,然后這個follower可能會被選舉為leader并且覆蓋這些日志條目;因此,不同的狀態機可能會執行不同的指令序列。
我們通過在leader election增加限制來完善raft算法。在任何基于leader的一致性算法中,leader都必須存儲所有已經提交的日志條目(或者通過額外的機制保證leader丟失的日志條目可以同步給leader)。Raft 使用了一種更加簡單的方法,它可以保證所有之前的任期號中已經提交的日志條目在選舉的時候都會出現在新的leader中,不需要傳送這些日志條目給leader。這意味著日志條目的傳送是單向的,只從leader傳給跟隨者,并且leader從不會覆蓋自身本地日志中已經存在的條目。
**由于raft節點中的日志是順序添加的,那么raft在leader election的時候通過比較兩份日志中最后一條日志目錄的索引號index和任期號term,就可以判斷出誰的日志更新。**只有最新的人才有權利作為leader。候選人為了贏得選舉必須聯系集群中的大部分節點,這意味著每一個已經提交額度日志條目至少會存在于這些節點中的至少一個上。如果候選人的日志至少和大多數的服務器節點一樣新,那么他一定持有了所有已經提交的日志條目。
log recovery
以下摘自baidu braft文章
Log Recovery這里分為current Term修復和prev Term修復,Log Recovery就是要保證一定已經Committed的數據不會丟失,未Committed的數據轉變為Committed,但不會因為修復過程中斷又重啟而影響節點之間一致性。
- current Term修復主要是解決某些Follower節點重啟加入集群,或者是新增Follower節點加入集群,Leader需要向Follower節點傳輸漏掉的Log Entry,如果Follower需要的Log Entry已經在Leader上Log Compaction清除掉了,Leader需要將上一個Snapshot和其后的Log Entry傳輸給Follower節點。
- prev Term修復主要是在保證Leader切換前后數據的一致性。通過上面RAFT的選主可以看出,每次選舉出來的Leader一定包含已經committed的數據(抽屜原理,選舉出來的Leader是多數中數據最新的,一定包含已經在多數節點上commit的數據),新的Leader將會覆蓋其他節點上不一致的數據。雖然新選舉出來的Leader一定包括上一個Term的Leader已經Committed的Log Entry,但是可能也包含上一個Term的Leader未Committed的Log Entry。這部分Log Entry需要轉變為Committed,相對比較麻煩,需要考慮Leader多次切換且未完成Log Recovery,需要保證最終提案是一致的,確定的。 **RAFT中增加了一個約束:對于之前Term的未Committed數據,修復到多數節點,且在新的Term下至少有一條新的Log Entry被復制或修復到多數節點之后,才能認為之前未Committed的Log Entry轉為Committed。**下圖就是一個prev Term Recovery的過程:
為了避免4-1中的現象,協議又強化了一個限制:
- 只有當前 Term 的 LogEntry 提交條件為:滿足多數派響應之后(一半以上節點 Append LogEntry 到日志)設置為 commit;
- 前一輪 Term 未 Commit 的 LogEntry 的 Commit 依賴于高輪 Term LogEntry 的 Commit。
也就是說,我們要通過提交 NO-OP LogEntry 提交系統可用性
在 Leader 通過競選剛剛成為 Leader 的時候,有一些等待提交的 LogEntry (即 SN > CommitPt 的 LogEntry),有可能是 Commit 的,也有可能是未 Commit 的(PS: 因為在 Raft 協議中 CommitPt 不用實時刷盤)。
所以為了防止出現非線性一致性(Non Linearizable Consistency);即之前已經響應客戶端的已經 Commit 的請求回退,并且為了避免出現上面4-1中的 Corner Case(已經commit的日志被覆蓋),往往我們需要通過下一個 Term 的 LogEntry 的 Commit 來實現之前的 Term 的 LogEntry 的 Commit (隱式commit),才能保障提供線性一致性。
但是有可能接下來的客戶端的寫請求不能及時到達,那么為了保障 Leader 快速提供讀服務,系統可首先發送一個 no-op LogEntry 來保障快速進入正常可讀狀態。
為什么no-op能解決不一致的問題?
剛當選的leader發送no-op,如果這個no-op可以commit成功那么可以認為這個節點之前所有的日志都已經commit成功了。換句話說,大部分節點已經將當前這個leader寫入了log中。
也就是說,通過這個no-op commit,我們commit了leader節點上所有的log。就算后面再有figure8中的覆蓋問題,覆蓋的也是這個no-op
詳情可以看我寫的另外一篇文章:raft引入no-op解決了什么問題
成員變更
一文看盡 Raft 一致性協議的關鍵點
可從數學上嚴格證明,只要每次只允許增加或刪除一個成員,Cold與Cnew不可能形成兩個不相交的多數派。這種方式比較簡單,也是etcd的做法。
如果每次增加超過兩個。會導致變更過程中出現多個多數派,所以要引入用兩階段成員變更
系統狀態變化同paxos一樣通過日志同步
add node
catch up
transition Cold和Cnew需要同時達成多數派,log replication才算是成功。
Single mempership change
論文中提以下幾個關鍵點:
注意:
raft用到的隨機時間
raft 腦裂
Raft 協議強依賴 Leader 節點的可用性來確保集群數據的一致性。數據的流向只能從 Leader 節點向 Follower 節點轉移。當 Client 向集群 Leader 節點提交數據后,Leader 節點接收到的數據處于未提交狀態(Uncommitted),接著 Leader 節點會并發向所有 Follower 節點復制數據并等待接收響應,確保至少集群中超過半數節點已接收到數據后再向 Client 確認數據已接收。一旦向 Client 發出數據接收 Ack 響應后,表明此時數據狀態進入已提交(Committed),Leader 節點再向 Follower 節點發通知告知該數據狀態已提交。
在這個過程中,主節點可能在任意階段掛掉,看下 Raft 協議如何針對不同階段保障數據一致性的。
數據到達 Leader 節點前
這個階段 Leader 掛掉不影響一致性,不多說。
數據到達 Leader 節點,但未復制到 Follower 節點
這個階段 Leader 掛掉,數據屬于未提交狀態,Client 不會收到 Ack 會認為超時失敗可安全發起重試。Follower 節點上沒有該數據,重新選主后 Client 重試重新提交可成功。原來的 Leader 節點恢復后作為 Follower 加入集群重新從當前任期的新 Leader 處同步數據,強制保持和 Leader 數據一致。
數據到達 Leader 節點,成功復制到 Follower 所有節點,但還未向 Leader 響應接收
這個階段 Leader 掛掉,雖然數據在 Follower 節點處于未提交狀態(Uncommitted)但保持一致,重新選出 Leader 后可完成數據提交,此時 Client 由于不知到底提交成功沒有,可重試提交。針對這種情況 Raft 要求 RPC 請求實現冪等性(idempotent),也就是要實現內部去重機制(client如果發了個請求,就要一直無限重試知道成功,并且raft集群也會在自己恢復之前嘗試commit但是可能因為節點crash沒有commit成功的log(Figure 8)。也就是說raft節點會忽略自己已經commit的log的rpc請求,直接返回成功?)。
數據到達 Leader 節點,成功復制到 Follower 部分節點,但還未向 Leader 響應接收
這個階段 Leader 掛掉,數據在 Follower 節點處于未提交狀態(Uncommitted)且不一致,Raft 協議要求投票只能投給擁有最新數據的節點。所以擁有最新數據的節點會被選為 Leader 再強制同步數據到 Follower,數據不會丟失并最終一致。
疑問:
總結
以上是生活随笔為你收集整理的一致性协议raft详解(二):安全性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一致性协议raft详解(一):raft整
- 下一篇: 一致性协议raft详解(三):raft中