1. 程式人生 > >Raft協議詳解(三)日誌複製

Raft協議詳解(三)日誌複製

在這一部分,我們講Raft幾個子問題中的日誌複製問題。主要內容是講Raft為什麼要進行日誌複製,以及如何進行日誌複製的。日誌複製(log replication)是leader的主要工作之一。在前面的第一、二部分,我們講到了日誌(log)是Raft的一致性保證非常重要的組成部分,很大程度Raft是利用log做的資料一致性。那麼我們就來說說日誌。

日誌是啥

這裡寫圖片描述

上圖是Raft論文中的圖片,我們可以之間看。每臺伺服器上都有自己的本地log,圖中一行是一個伺服器。對於一臺伺服器上的log,可以理解為一個list。list裡面有兩個元素,一個是term,一個是操作。結構大概是這樣:

{
   term1:operation1,
   term2:operation2
   ……
}

當然,list也有索引(log index),如圖最上面的1,2,3,4……。那麼日誌就長這樣,儲存著每個任期的每個操作。一個“term1:operation1”組合,被叫做一個entry。當leader認為log裡面儲存的entry操作可以被執行時,那麼就會執行log裡面的操作了。

假如說,每臺伺服器上的log內容都是一樣的,那麼執行log操作(也叫提交(commit))的時後,伺服器裡面的狀態機(state machin,見Raft協議詳解一)的內容也一定是一樣的,因為狀態機的內容就是log執行的結果。換句話說,我只要保證了每臺伺服器上的log內容一致,那麼也就保證了資料(狀態機內容)的一致了。

那麼,如何保證日誌一致呢?

日誌的一致性保證

在Raft協議中有兩個主要的訊息,一個是在第二節講到的RequestVote RPC,用於選主投票時leader發出的訊息。一個就是AppendEntries RPC,用於心跳和日誌複製。對於心跳,只需要傳送空內容的AppendEntries RPC就可以了,我們主要關注日誌複製的訊息,看看Raft是怎麼操作的。

1.leader接受客戶端的操作請求,如“將X複製為3”。假如leader當前的任期為term1,那麼leader就會向自己本地log的索引K的位置新增一個entry:“term1:X賦值為3”。之所以新增到K是因為K索引位置之前已經有了log內容了。
2.leader向叢集中其他follower並行傳送AppendEntries RPC訊息。注意點來了!敲黑板。關鍵就是這個訊息裡面包含哪些內容。有兩部分組成。一:這個新的entry:“term1:X賦值為3”。二:K-1,以及K-1索引位置的entry。
3.當一個follower收到一個AppendEntries RPC訊息時會檢視自己本地的log中的K-1位置的entry的內容。

  • 假如本地log中K-1位置的entry內容與接收到的來自leader的K-1的entry內容一致,那麼就將leader發來的K位置的entry儲存在自己的K位置(當然要做併發控制),並返回true,告訴leader儲存成功了
  • 假如本地log中K-1位置的entry內容與接收到的來自leader的K-1的entry內容不一致,那麼就返回false,告訴leader不一致。leader收到訊息後,會將K減小一點,然後再次重新發AppendEntries RPC,直到follower返回true。因為到K=0的時候肯定會一樣(都沒有),因此早晚一定會得到true的回覆。此時leader將匹配的位置和最新的位置中間的內容都發送給follower,follower會將接收到的內容覆蓋到對應的位置。

4.經過第3步的操作,此時follower和leader的日誌就已經一樣了。

當leader收到了大多數的follower的true的返回,那麼leader就可以回覆客戶端,已經成功更新了資料。

在上面第3步的過程中,需要不斷去嘗試K的正確位置,因此會發送比較多的AppendEntries RPC的訊息。所以是可以在此處進行優化的。

其他

在Raft中,一切以leader為主。因此日誌不是最新的話,就不能成為leader。因此在選主的時候,會進行日誌比較。假如在投票階段,一個follower收到的選主請求中,包含的日誌資訊比自己的要舊,那麼也會拒絕給這個請求投贊成票。如何比較新舊呢?一是看任期term,一是看最後一個entry的索引號。任期大的新,任期相同的索引大的新。

此外,假如leader在準備commit一個得到大多數follower的true的entry時,宕機了。那麼後面的leader如何保證前面的leader的entry被commit呢?實際上,一條 entry被準備commit的前提是,它已經被複制到了多臺伺服器上了。由於leader的選舉,會選擇有比較新的log的伺服器,那麼擁有上一個leader的entry的,那一定時叢集中最新的。也就是說擁有上一個leader的entry內容的伺服器才能成為leader。那麼後面的leader在不知道上一個leader有沒有提交的情況下,怎麼操作呢?(我猜可以用一個變數儲存上一次執行到哪個log index位置了,後面的接著執行。具體我沒看懂論文裡怎麼說的)

以上就是日誌複製問題了。後面我們講第三個:維護成員變化(membership changes)