分散式技術專題-分散式協議演算法-帶你徹底認識Paxos演算法、Zab協議和Raft協議的原理和本質

李浩宇Alex發表於2021-09-05

內容簡介指南

  • Paxo演算法指南

  • Zab演算法指南

  • Raft演算法指南


Paxo演算法指南

Paxos演算法的背景

【Paxos演算法】是萊斯利·蘭伯特(Leslie Lamport)1990年提出的一種基於訊息傳遞的一致性演算法,是目前公認的解決分散式一致性問題最有效的演算法之一,其解決的問題就是在分散式系統中如何就某個值(決議)達成一致。

Paxos演算法的前提

Paxos演算法的前提假設是不存在拜占庭將軍問題,即:通道是安全的(通道可靠),發出的訊號不會被篡改。

Paxos演算法的介紹

在Paxos演算法中,有三種角色:

  • proposer:提案Proposal提出者。
  • Acceptor:決策者,可以批准議案。
  • Learner:最終決策的學習者。

Paxos演算法安全性前提如下:
  • 只有被提出的value才能被選定。

  • 只有一個value被選定。

  • 如果某個程式認為某個value被選定了,那麼這個value必須是真的被選定的那個。

Paxos演算法的過程描述:

Paxos演算法類似於兩階段提提交,其演算法執行過程分為兩個階段。具體如下

prepare階段
  • proposer提出一個編號為N的proposal,傳送給半數以上的acceptor
  • acceptor收到編號為N的prepare請求後:
    • 如果小於它已經響應過的請求,則拒絕回覆或者回復error;
    • 如果N大於該Acceptor已經響應過的所有Prepare請求的編號,那麼它就會將它已經接受過(已經經過第二階段accept的提案)的編號最大的提案(如果有的話,如果還沒有的accept提案的話返回{pok,null,null})作為響應反饋給Proposer,同時儲存更新本地對應的提案編號,並且該Acceptor承諾不再接受任何編號小於N的提案。
    • 分為兩種情況
      • 如果acceptor已經接受過提案,返回接受提案的最大value;
      • 如果還沒有接受過提案,就返回{pok,null,null})
accept階段
  • 如果proposer收到半數以上的acceptor回覆的編號為N的提案的prepare響應,那麼會傳送一個針對[N,V]提案的Accept請求給半數以上的Acceptor。

注意:V就是收到的響應中編號最大的提案的value。

  • 如果響應中不包含任何提案,那麼V就由Proposer自己決定,可以是任意值。

  • 如果Acceptor收到一個針對編號為N的提案的Accept請求,只要該Acceptor沒有對編號大於N的Prepare請求做出過響應,它就接受該提案。

  • 如果N小於Acceptor以及響應的prepare請求,則拒絕,不回應或回覆error(當proposer沒有收到過半的回應,那麼他會重新進入第一階段,遞增提案號,重新提出prepare請求)。

具體如下圖所示:

過半的acceptor都接受提案後,learner會自動感知到,並開始學習提案。(同一個程式可以同時扮演多個角色)

Learner的學習過程

learner學習過程包含兩種場景:
  • Learner所在節點參與了提案選舉,Learner需要知道其接受(accept)的提案值是否被選中(chosen)。
  • Learner所在節點已落後於其他節點,Learner需要選擇合適的策略快速完成追趕,並重新參與到提案選舉當中。
選中通知
  • 本節點Proposer的某個提案被選中(chosen)時,通過(MsgType_PaxosLearner_ProposerSendSuccess)訊息通知到各個節點。

  • 正常情況下,所有節點處於online狀態,共同參與paxos選舉。因此為了避免instance id衝突,paxos建議只由主節點的proposer發起提案,這樣保證接受提案和習得提案編號一致。

  • 此時,Learn習得的提案值實際上就是本節點Accept的資料,因此learner只更新記憶體狀態即可,無需再次落盤(acceptor已落盤)。

  • 最後,如果存在follower節點,資料同步到follower(follower節點不參與paxos演算法,相當於某個paxos節點的同步備)。

提案值追趕
  • 一旦節點處於落後狀態,它無法再參與到paxos提案選舉中來。這時需要由learner發起主動學習完成追趕。

  • Paxos啟動時,啟動learner定時器,定時傳送learn請求到各個節點,傳送請求攜帶本節點的Instance ID、Node ID資訊。各節點收到該請求後,回覆資料自主完成學習過程。

資料各節點保持資料一致性

為什麼要有兩段提交

  • 一方面,第一次預提交後可能被告知已經有觀點了,此時他不應該提出自己的觀點,而應該儘快收斂,支援最新的觀點。
  • 另一方面,進行預加鎖。

怎麼保證Proposal編號唯一

  • 假設有K臺Server執行paxos演算法,那麼他們初始編號為0…k-1。以後編號每次增加k,從而保證全域性唯一遞增。

  • 正式提案被半數以上Acceptor接受後,就可以確定最終被接受的提案就是該觀點。

  • 兩個半數以上的集合的一定存在交集。

Paxos演算法有活鎖問題存在。

介紹了Paxos的演算法邏輯,但在演算法執行過程中,可能還會存在一種極端情況,當有兩個proposer依次提出一系列編號遞增的議案,那麼會陷入死迴圈,無法完成第二階段,也就是無法選定一個提案。如下圖:

Paxos演算法的過半依據

  • 在Paxos演算法中,採用了“過半”理念,也就是少數服從多數,這使Paxos演算法具有很好的容錯性。那麼為什麼採用過半就可以呢?
  • Paxos基於的過半數學原理: 我們稱大多數(過半)程式組成的集合為法定集合, 兩個法定(過半)集合必然存在非空交集,即至少有一個公共程式,稱為法定集合性質。 例如A,B,C,D,F程式組成的全集,法定集合Q1包括程式A,B,C,Q2包括程式B,C,D,那麼Q1和Q2的交集必然不在空,C就是Q1,Q2的公共程式。如果要說Paxos最根本的原理是什麼,那麼就是這個簡單性質。也就是說:兩個過半的集合必然存在交集,也就是肯定是相等的,也就是肯定達成了一致。
  • Paxos是基於訊息傳遞的具有高度容錯性的分散式一致性演算法。Paxos演算法引入了過半的概念,解決了2PC,3PC的太過保守的缺點,且使演算法具有了很好的容錯性,另外Paxos演算法支援分散式節點角色之間的輪換,這極大避免了分散式單點的出現,因此Paxos演算法既解決了無限等待問題,也解決了腦裂問題,是目前來說最優秀的分散式一致性演算法。其中,Zookeeper的ZAB演算法和Raft一致性演算法都是基於Paxos的。在後邊的文章中,我會逐步介紹優秀的分散式協調服務框架,也是極優秀的工業一致性演算法的實現Zookeeper使用和實現。

ZAB演算法指南

Zookeeper 採用的 ZAB協議也是基於 Paxos 演算法實現的,不過 ZAB 對 Paxos 進行了很多改進與優化,兩者的設計目標也存在差異——ZAB 協議主要用於構建一個高可用的分散式資料主備系統,而 Paxos 演算法則是用於構建一個分散式的一致性狀態機系統。

ZAB協議全稱就是ZooKeeper Atomic Broadcast protocol,是ZooKeeper用來實現一致性的演算法,分成如下3個階段

  • fast leader election:快速選舉階段
  • recovery:恢復階段
    • Discovery;
    • Synchrozation
  • broadcasting:廣播階段

先了解一些基本概念

  • electionEpoch:每執行一次leader選舉,electionEpoch就會自增,用來標記leader選舉的輪次
  • peerEpoch:每次leader選舉完成之後,都會選舉出一個新的peerEpoch,用來標記事務請求所屬的輪次
  • zxid:事務請求的唯一標記,由leader伺服器負責進行分配高32位是上述的peerEpoch,低32位是請求的計數,從0開始。
  • lastprocessZxid:最後一次commit的事務請求的zxid
  • LinkedList committedLog、long maxCommittedLog、long minCommittedLog:ZooKeeper會儲存最近一段時間內執行的事務請求議案,個數限制預設為500個議案。committedLog就是用來儲存議案的列表,maxCommittedLog表示最大議案的zxid,minCommittedLog表示committedLog中最小議案的zxid。
  • ConcurrentMap<Long, Proposal> outstandingProposals:Leader擁有的屬性,每當提出一個議案,都會將該議案存放至outstandingProposals,一旦議案被過半認同了,就要提交該議案,則從outstandingProposals中刪除該議案。
  • ConcurrentLinkedQueue toBeApplied:Leader擁有的屬性,每當準備提交一個議案,就會將該議案存放至該列表中,一旦議案應用到ZooKeeper的記憶體樹中了,然後就可以將該議案從toBeApplied中刪除。
  • state:當前伺服器的狀態
  • recvQueue:訊息接收佇列,用於存放那些從其他伺服器接收到的訊息。
  • queueSendMap:訊息傳送佇列,用於儲存那些待傳送的訊息,按照SID進行分組。
  • senderWorkerMap:傳送器集合,每個SenderWorker訊息傳送器,都對應一臺遠端Zookeeper伺服器,負責訊息的傳送,也按照SID進行分組。
  • lastMessageSent:最近傳送過的訊息,為每個SID保留最近傳送過的一個訊息。
在ZAB協議中,服務的的狀態state有四種:
  • LOOKING:進入leader選舉狀態
  • FOLLOWING:leader選舉結束,進入follower狀態
  • LEADING:leader選舉結束,進入leader狀態
  • OBSERVING:處於觀察者狀態

協議演算法的具體描述

Broadcasting過程
  1. leader針對客戶端的事務請求,創造出一個提案,zxid由leader決定,並將該提案的zxid,提案放到outstandingProposals Map中。
  2. leader向所有的follower傳送該提案,如果過半的follower回覆OK的話,則leader認為可以提交該議案,則將該議案從outstandingProposals中刪除。
  3. 然後存放到toBeApplied中leader對該議案進行提交,會向所有的follower傳送提交該議案的命令,leader自己也開始執行提交過程,會將該請求的內容應用到ZooKeeper的記憶體樹中。
  4. 然後更新lastProcessedZxid為該請求的zxid,同時將該請求的議案存放到上述committedLog,同時更新maxCommittedLog和minCommittedLog。
  5. leader回覆客戶端,並將提案中ToBeApplied中刪除
fast leader election過程

選舉過程關注兩個要點:剛啟動時進行leader選舉和選舉完leader後,剛啟動的server怎麼感知到leader,投票過程有兩個比較重要的資料:

  • HashMap<Long, Vote> recvset:用於收集LOOKING、FOLLOWING、LEADING狀態下的server的投票
  • HashMap<Long, Vote> outofelection:用於收集FOLLOWING、LEADING狀態下的server的投票(說明leader選舉已經完成)
具體的過程有:
  1. 伺服器先自增electionEpoch,給自己投票:
  • 從快照日誌和事務日誌中載入資料,得到本機器的記憶體樹資料,以及lastProcessedZxid。投票內容為:
    • proposedLeader:server自身的myid值,初始為本機器的id
    • proposedZxid:最大事務zxid,初始為本機器的lastProcessedZxid
    • proposedEpoch:peerEpoch值,由上述的lastProcessedZxid的高32得到
    • 然後向所有的伺服器傳送投票。
  1. server接收到投票通知後,進行PK。

    • 如果收到的通知中的electionEpoch比自己的大,則更新自己的electionEpoch為serverA的electionEpoch;
    • 如果、收到的通知中的electionEpoch比自己的小,則向serverA傳送一個通知,將自己的投票以及electionEpoch傳送給serverA,serverA收到後就會更新自己的electionEpoch。
    • 如果electionEpoch相同,PK的規則是proposedZxid,然後再是myId
  2. 根據server的狀態來判定leader

    • 如果當前發來的投票的server的狀態是LOOKING狀態,則只需要判斷本機器的投票是否在recvset中過半了,如果過半了則說明leader選舉就算成功了,如果當前server的id等於上述過半投票的proposedLeader,則說明自己將成為了leader,否則自己將成為了follower。

    • 如果當前發來的投票的server的狀態是FOLLOWING、LEADING狀態,則說明leader選舉過程已經完成了,則發過來的投票就是leader的資訊,這裡就需要判斷髮過來的投票是否在recvset或者outofelection中過半了,同時還要檢查leader是否給自己傳送過投票資訊,從投票資訊中確認該leader是不是LEADING狀態。

Recovery過程

一旦leader選舉完成,就開始進入恢復階段,就是follower要同步leader上的資料資訊。

  1. 通訊初始化

leader會建立一個ServerSocket,接收follower的連線,leader會為每一個連線會用一個LearnerHandler執行緒來進行服務;

  1. 重新為peerEpoch選舉出一個新的peerEpoch

    • follower會向leader傳送一個Leader,FOLLOWERINFO資訊,包含自己的peerEpoch資訊。

    • leader的LearnerHandler會獲取到上述peerEpoch資訊,從中選出一個最大的peerEpoch,然後加1作為新的peerEpoch。

    • 然後leader的所有LearnerHandler會向各自的follower傳送一個Leader.LEADERINFO資訊,包含上述新的peerEpoch;

    • follower會使用上述peerEpoch來更新自己的peerEpoch,同時將自己的lastProcessedZxid發給leader,leader的根據這個lastProcessedZxid和leader的lastProcessedZxid之間的差異進行同步。

  2. 已經處理的事務議案的同步

    • 判斷LearnerHandler中的lastProcessedZxid是否在minCommittedLog和maxCommittedLog之間

    • LearnerHandler中的lastProcessedZxid和leader的lastProcessedZxid一致,則說明已經保持同步了

    • 如果lastProcessedZxid在minCommittedLog和maxCommittedLog之間,從lastProcessedZxid開始到maxCommittedLog結束的這部分議案,重新傳送給該LearnerHandler對應的follower,同時傳送對應議案的commit命令。

上述可能存在一個問題:即lastProcessedZxid雖然在他們之間,但是並沒有找到lastProcessedZxid對應的議案,即這個zxid是leader所沒有的,此時的策略就是完全按照leader來同步,刪除該follower這一部分的事務日誌,然後重新傳送這一部分的議案,並提交這些議案。

  • 如果lastProcessedZxid大於maxCommittedLog,則刪除該follower大於部分的事務日誌

  • 如果lastProcessedZxid小於minCommittedLog,則直接採用快照的方式來恢復。

  1. 未處理的事務議案的同步

    • LearnerHandler還會從leader的toBeApplied資料中將大於該LearnerHandler中的lastProcessedZxid的議案進行傳送和提交(toBeApplied是已經被確認為提交的)

    • LearnerHandler還會從leader的outstandingProposals中大於該LearnerHandler中的lastProcessedZxid的議案進行傳送,但是不提交(outstandingProposals是還沒被被確認為提交的)

  2. 將LearnerHandler加入到正式follower列表中

  3. LearnerHandler傳送Leader.NEWLEADER以及Leader.UPTODATE命令。

    • leader開始進入心跳檢測過程,不斷向follower傳送心跳命令,不斷檢是否有過半機器進行了心跳回復,如果沒有過半,則執行關閉操作,開始進入leader選舉狀態;
    • LearnerHandler向對應的follower傳送Leader.UPTODATE,follower接收到之後,開始和leader進入Broadcast處理過程。

事務持久化和恢復過程

  • 事務持久化分為:broadcasting持久化和leader shutdown過程的持久化。
    • leader針對每次事務請求都會生成一個議案,然後向所有的follower傳送該議案。follower收到提案後,將該議案記錄到事務日誌中,每當記滿100000個(預設),則事務日誌執行flush操作,同時開啟一個新的檔案來記錄事務日誌
    • 同時會執行記憶體樹的快照,snapshot.[lastProcessedZxid]作為檔名建立一個新檔案,快照內容儲存到該檔案中
    • 一旦leader過半的心跳檢測失敗,則執行shutdown方法,在該shutdown中會對事務日誌進行flush操作
事務的恢復分為快照恢復和日誌恢復。
  • 事務快照的恢復:會在事務快照檔案目錄下找到最近的100個快照檔案,並排序,最新的在前;對上述快照檔案依次進行恢復和驗證,一旦驗證成功則退出,否則利用下一個快照檔案進行恢復。恢復完成更新最新的lastProcessedZxid;
  • 事務日誌的恢復:從事務日誌檔案目錄下找到zxid大於等於上述lastProcessedZxid的事務日誌,然後對上述事務日誌進行遍歷,應用到ZooKeeper的記憶體樹中,同時更新lastProcessedZxid,同時將上述事務日誌儲存到committedLog中,並更新maxCommittedLog、minCommittedLog

Raft演算法指南

Raft背景

在分散式系統中,一致性演算法至關重要。在所有一致性演算法中,Paxos 最負盛名,它由萊斯利·蘭伯特(Leslie Lamport)於 1990 年提出,是一種基於訊息傳遞的一致性演算法,被認為是類似演算法中最有效的。

Paxos演算法雖然很有效,但複雜的原理使它實現起來非常困難,截止目前,實現 Paxos 演算法的開源軟體很少,比較出名的有 Chubby、LibPaxos。


  • 由於Paxos演算法過於複雜、實現困難,極大地制約了其應用,而分散式系統領域又亟需一種高效而易於實現的分散式一致性演算法,在此背景下,Raft 演算法應運而生。

  • Raft是一個共識演算法(consensus algorithm),所謂共識,就是多個節點對某個事情達成一致的看法,即使是在部分節點故障、網路延時、網路分割的情況下。

  • 共識演算法的實現一般是基於複製狀態機(Replicated state machines),何為複製狀態機:簡單來說:相同的初識狀態 + 相同的輸入 = 相同的結束狀態。

Raft 角色

一個Raft叢集包含若干個節點,這些節點分為三種狀態:Leader、 Follower、Candidate,每種狀態負責的任務也是不一樣的。正常情況下,叢集中的節點只存在 Leader與Follower兩種狀態。

  • leader:負責日誌的同步管理,處理來自客戶端的請求,與Follower保持heartBeat的聯絡;
  • follower:響應 Leader 的日誌同步請求,響應Candidate的邀票請求,以及把客戶端請求到Follower的事務轉發(重定向)給Leader;
  • candidate:負責選舉投票,叢集剛啟動或者Leader當機時,狀態為Follower的節點將轉為Candidate併發起選舉,選舉勝出(獲得超過半數節點的投票)後,從Candidate轉為Leader狀態。

還有一個關鍵概念:term(任期)。以選舉(election)開始,每一次選舉term都會自增,充當了邏輯時鐘的作用。

Raft的3個子問題

為簡化邏輯和實現,Raft 將一致性問題分解成了三個相對獨立的子問題。

  • 選舉(Leader Election):當 Leader 當機或者叢集初創時,一個新的 Leader 需要被選舉出來;
  • 日誌複製(Log Replication):Leader 接收來自客戶端的請求並將其以日誌條目的形式複製到叢集中的其它節點,並且強制要求其它節點的日誌和自己保持一致;
  • 安全性(Safety):如果有任何的伺服器節點已經應用了一個確定的日誌條目到它的狀態機中,那麼其它伺服器節點不能在同一個日誌索引位置應用一個不同的指令。

選舉過程

如果follower在election timeout內沒有收到來自leader的心跳,則會主動發起選舉。

第一階段:所有節點都是 Follower。

Raft 叢集在剛啟動(或 Leader 當機)時,所有節點的狀態都是 Follower,初始 Term(任期)為 0。同時啟動選舉定時器,每個節點的選舉定時器超時時間都在 100~500 毫秒之間且並不一致。

第二階段:Follower 轉為 Candidate 併發起投票。

沒有leader後,followers狀態自動轉為candidate,並向叢集中所有節點傳送投票請求並且重置選舉定時器。

投票過程有:
  • 增加節點本地的current term ,切換到candidate狀態
  • 投自己一票,並行給其他節點傳送 RequestVote RPCs
  • 等待其他節點的回覆,可能出現三種結果:
    • 收到majority的投票(含自己的一票),則贏得選舉,成為leader
    • 被告知別人已當選,那麼自行切換到follower
    • 一段時間內沒有收到majority投票,則保持candidate狀態,重新發出選舉
第三階段:投票策略
投票的約束條件有:
  • 在一個term內,一個節點只允許發出一次投票;
  • 候選人知道的資訊不能比自己的少(這一部分,後面介紹log replication和safety的時候會詳細介紹)
  • first-come-first-served 先來先得
  • 如果參加選舉的節點是偶數個,raft通過randomized election timeouts來儘量避免平票情況,也要求節點的數目都是奇數個,儘量保證majority的出現。

log Replication原理

  • 當leader選舉成功後,客戶端所有的請求都交給了leader,leader排程請求的順序性和followers的狀態一致性。

  • 在叢集中,所有的節點都可能變為leader,為了保證後續leader節點變化後依然能夠使叢集對外保持一致,需要通過Log Replication機制來解決如下兩個問題:

  • Follower與Leader節點相同的順序依次執行每個成功提案;

  • 每個成功提交的提案必須有足夠多的成功副本,來保證後續的訪問一致

第一階段:客戶端請求提交到 Leader。

Leader 在收到client請求提案後,會將它作為日誌條目(Entry)寫入本地log中。需要注意的是,此時該 Entry 的狀態是未提交(Uncommitted),Leader 並不會更新本地資料,因此它是不可讀的。

第二階段:Leader 將 Entry 傳送到其它 Follower
  • Leader 與 Floolwers 之間保持著心跳聯絡,隨心跳 Leader 將追加的 Entry(AppendEntries)並行地傳送給其它的 Follower,並讓它們複製這條日誌條目,這一過程稱為複製(Replicate)。

  • 為什麼 Leader 向 Follower 傳送的 Entry 是 AppendEntries,因為 Leader 與 Follower 的心跳是週期性的,而一個週期間 Leader 可能接收到多條客戶端的請求,因此,隨心跳向 Followers 傳送的大概率是多個 Entry,即 AppendEntries。

  • Leader 向 Followers 傳送的不僅僅是追加的 Entry(AppendEntries)在傳送追加日誌條目的時候,Leader 會把新的日誌條目緊接著之前條目的索引位置(prevLogIndex), Leader 任期號(Term)也包含在其中。如果 Follower 在它的日誌中找不到包含相同索引位置和任期號的條目,那麼它就會拒絕接收新的日誌條目,因為出現這種情況說明 Follower 和 Leader 不一致。

  • 如何解決 Leader 與 Follower 不一致的問題,正常情況下,Leader 和 Follower 的日誌保持一致。然而,Leader 和 Follower 一系列崩潰的情況會使它們的日誌處於不一致狀態。

有三種情況:
  1. Follower落後新的leader,丟失一些在新的 Leader 中有的日誌條目
  2. Follower領先新的leader,有一些 Leader 沒有的日誌條目,
  3. 或者兩者都發生。丟失或者多出日誌條目可能會持續多個任期。

要使 Follower 的日誌與 Leader 恢復一致,Leader 必須找到最後兩者達成一致的地方(就是回溯,找到兩者最近的一致點),然後刪除從那個點之後的所有日誌條目,傳送自己的日誌給 Follower。Leader 為每一個 Follower 維護一個 nextIndex,它表示下一個需要傳送給 Follower 的日誌條目的索引地址。當一個 Leader 剛獲得權力的時候,它初始化所有的 nextIndex 值,為自己的最後一條日誌的 index 加 1。如果一個 Follower 的日誌和 Leader 不一致,那麼在下一次附加日誌時一致性檢查就會失敗。在被 Follower 拒絕之後,Leader 就會減小該 Follower 對應的 nextIndex 值並進行重試。最終 nextIndex 會在某個位置使得 Leader 和 Follower 的日誌達成一致。當這種情況發生,附加日誌就會成功,這時就會把 Follower 衝突的日誌條目全部刪除並且加上 Leader 的日誌。一旦附加日誌成功,那麼 Follower 的日誌就會和 Leader 保持一致,並且在接下來的任期繼續保持一致。

第三階段:Leader 等待 Followers 回應。

Followers 接收到 Leader 發來的複製請求後,有兩種可能的回應:

  • 寫入本地日誌中,返回 Success;

  • 一致性檢查失敗,拒絕寫入,返回 False,原因和解決辦法上面已做了詳細說明。

  • 當 Leader 收到大多數 Followers 的回應後,會將第一階段寫入的 Entry 標記為提交狀態(Committed),並把這條日誌條目應用到它的狀態機中。

第四階段:Leader 回應客戶端。

完成前三個階段後,Leader會向客戶端回應 OK,表示寫操作成功。

第五階段,Leader 通知 Followers Entry 已提交

Leader 回應客戶端後,將隨著下一個心跳通知 Followers,Followers 收到通知後也會將 Entry 標記為提交狀態。至此,Raft 叢集超過半數節點已經達到一致狀態,可以確保強一致性。

raft safety保證

1) election safety: 在一個term內,至多有一個leader被選舉出來。raft演算法通過

一個節點某一任期內最多隻能投一票;
只有獲得majority投票的節點才會成為leader。
2)log matching:說如果兩個節點上的某個log entry的log index相同且term相同,那麼在該index之前的所有log entry應該都是相同的。leader在某一term的任一位置只會建立一個log entry,且log entry是append-only。

3)consistency check。leader在AppendEntries中包含最新log entry之前的一個log 的term和index,如果follower在對應的term index找不到日誌,那麼就會告知leader不一致。當出現了leader與follower不一致的情況,leader強制follower複製自己的log。

3)leader completeness :如果一個log entry在某個任期被提交(committed),那麼這條日誌一定會出現在所有更高term的leader的日誌裡面。

一個日誌被複制到majority節點才算committed
一個節點得到majority的投票才能成為leader,而節點A給節點B投票的其中一個前提是,B的日誌不能比A的日誌舊。
4)stale leader: 落後的leader,但在網路分割(network partition)的情況下,可能會出現兩個leader,但兩個leader所處的任期是不同的。而在raft的一些實現或者raft-like協議中,leader如果收不到majority節點的訊息,那麼可以自己step down,自行轉換到follower狀態。

5)leader crash:新的節點成為Leader,為了不讓資料丟失,希望新Leader包含所有已經Commit的Entry。為了避免資料從Follower到Leader的反向流動帶來的複雜性,Raft限制新Leader一定是當前Log最新的節點,即其擁有最多最大term的Log Entry。

6)State Machine Safety

某個leader選舉成功之後,不會直接提交前任leader時期的日誌,而是通過提交當前任期的日誌的時候“順手”把之前的日誌也提交了,具體的實現是:如果leader被選舉後沒有收到客戶端的請求呢,論文中有提到,在任期開始的時候發立即嘗試複製、提交一條空的log。

總結:raft將共識問題分解成兩個相對獨立的問題,leader election,log replication。流程是先選舉出leader,然後leader負責複製、提交log(log中包含command)

log replication約束:

一個log被複制到大多數節點,就是committed,保證不會回滾
leader一定包含最新的committed log,因此leader只會追加日誌,不會刪除覆蓋日誌
不同節點,某個位置上日誌相同,那麼這個位置之前的所有日誌一定是相同的
Raft never commits log entries from previous terms by counting replicas.

相關文章