當leader收到客戶的寫請求後, 它記錄一個更新log, 然後把log並行的複製給其它server。在多數派完成log複製後, leader向客戶回覆操作成功,以確認操作被提交。如果有follower宕機或處理的速度很慢, leader會一直重試直到成功。我們先看log的組織形式。以term 和 index來標示log 如下圖:
1 2 3 4 5 6 7 8 log index
leader 1,x=3 1,y=1 1,y=9 2,x=2 3,x=0 3,x=7 3,x=5 3,x=4 term number, operation
follower 1,x=3 1,y=1 1,y=9 2,x=2 3,x=0
follower 1,x=3 1,y=1 1,y=9 2,x=2 3,x=0 3,x=7 3,x=5 3,x=4
follower 1,x=3 1,y=1
follower 1,x=3 1,y=1 1,y=9 2,x=2 3,x=0 3,x=7 3,x=5
|------------------------------------------提交掉的log-----------------------------------------|
上面的示意圖慘點, 不過意思到了。
一條log什麼時候被確認是提交的, 這個由leader來決定。 已經提交的log不會丟失。 ledaer把一條log複製到了多數派就可以認爲這條log被提交了, 同時在leader的log中, 這條log之前的log(即便是前任leader創建的)也就被提交了。 注意, 這裏的當前log應該是由當前leader創建的。 如果是前任留下來的log, 即便複製到了多數派也不能認爲提交。 這個後面專門討論。leader一直追蹤着提交了的最高的log index, 並且在後續的log複製消息中帶着這個信息, 這樣follower就知道哪條log被認爲提交了, 從而應用相應的操作到自己到狀態機中。leader在一個給定的term中給定的index, 只會至多創建一條log。 這個log在log序列中的位置永遠不會改變。leader在複製log給follower的時候, 總是會攜帶當前log的前一條log的term和index信息。 如果一個follower在自己本地信息中無法找到對應的log信息, 則拒絕這個複製log的請求。有了這個條件, 如同數學歸納法一樣, leader知道只要log複製成功了, 那麼follower的log序列就和自己的log序列是完全一致的。
在正常情況下, leader和followers的log序列是一致的, 上面這種檢查不會失敗。 但是如果有leader宕機, 或者leader和follower交錯宕機等複雜情況, 就會發生這種序列的不一致, 比如:
1 2 3 4 5 6 7 8 9 10 11 12 log index
L 1 1 1 4 4 5 5 6 6 6 term number
a 1 1 1 4 4 5 5 6 6
b 1 1 1 4
c 1 1 1 4 4 5 5 6 6 6 6
d 1 1 1 4 4 5 5 6 6 6 7 7
e 1 1 1 4 4 4 4
f 1 1 1 2 2 2 3 3 3 3 3
上圖中 L行是 term8 的leader, a -f 是可能的follower。 當L現在成爲leader的時候, follower 有可能缺失log(a-b), 有可能存在多出來的沒提交的log(c-d)也有可能兩者都有(e-f)。比如f的出現, 可能是因爲在term2的時候它是leader, 然後接受三個寫請求, 寫了三個log。在都沒有複製到其它follower的時候自己宕機, 然後迅速重啓, 又成爲了leader(這時term變爲3), 又接受了5個寫請求。 寫了5條term3的log, 然後宕機。
在Raft中, 如果leader發現了這種不一致, 它會要求把自己的log複製給follower從而讓follower跟自己達到一致。 這就是說, follower的log可能被覆寫(leader的從來不會被覆寫)。因爲leader在選舉的時候是有限制條件的(包含所有的已經提交的log), 所以這樣的同步是安全的。leader要找到follower跟自己一致的log, 然後刪除follower後面的log把自己的同步給follower。 leader是通過這種方式來做的: 首先, leader爲每個follower保存一個next index指針。用這個指針來標記leader應該發給follower的下一條log。 當一個server成爲leader的時候, 它初始化所有follower的這個指針爲自己最後一條log的下一條, 然後跟follower覈對(在複製log的消息中),如果不符合則前移指針, 直到找到符合的爲止。在找到並且同步好log之後, follower的log序列就應該跟leader完全一致了, 並且一直保持下去。
提交前任的log:
前面提到, 一個leader一旦把本term裏的一個log複製給了多數派就可以認爲這個log被提交了。當一個leader在完成多數派複製之前就宕掉, 下任leader會嘗試完成上一任的複製, 這時候複製到多數派後, leader不能確認這個log是提交的。 只有自己本term內又一個新的log複製到多數派後才能確認本term的log和之前的所有log(包括上任那個)是提交的。看下面的演示:
1 2 1 2 1 2 3 1 2 3 1 2 3
------------------------------------------
s1 1 2 | 1 2 | 1 2 4 | 1 3 | 1 2 4
s2 1 2 | 1 2 | 1 2 | 1 3 | 1 2 4
s3 1 | 1 | 1 2 | 1 3 | 1 2 4
s4 1 | 1 | 1 | 1 3 | 1
s5 1 | 1 3 | 1 3 | 1 3 | 1 3
a | b | c | d | e
解釋一下上面的圖。 s1是leader的時候, 將term2複製到s2就宕機了(a列的狀況)。 然後S5成爲candidate進入term3, 並且收到了S3,S4和它自己的投票,從而成爲term3的leader。它在index爲2的位置寫了一個不同的內容, 還沒複製出去就宕機(列b)。s1重啓, 並且被選爲leader。在成爲leader的過程中, 經過多數派投票, 所以S1知道term2 term3都是過時的了, 所以它的term是4, 這個時候S1繼續前面2的複製, 把2的log複製給了S3從而達到了多數派(列c),但是這個時候是不能認爲term2的log是提交了的, 因爲如果這個時候S1宕機, S5重啓併成爲leader(因爲S5當前term是3, 在S1宕機後, 它的term最大也就是log最新, 可以被選爲leader), S5會把自己的index 2位置term3的log複製給S2-S4, 從而覆蓋掉了term2的log(列d)。但是,
如果S1(列c狀態)完成了本term, 也就是term4的一條log到多數派到複製, 則可以認爲term2, term4都被提交了。 因爲這時s1再宕機, s5是無法被選成leader的(列e)因爲 s5不滿足必須包含所有提交log的約束(見leader選舉時, leader的log不能比任何其它server的舊的要求)。
log傳播完了, 最核心的部分也就都介紹完了。 其餘比如集羣成員變化, log壓縮等都不在這裏細說。感興趣都同學自行閱讀原文。