Raft協議學習筆記

一見發表於2019-01-23

目錄

目錄 1

1. 前言 1

2. 名詞 1

3. 什麼是分散式一致性? 3

4. Raft選舉 3

4.1. 什麼是Leader選舉? 3

4.2. 選舉的實現 4

4.3. Term和Lease比較 4

4.4. 選舉圖示 4

4.5. 選舉總結 7

5. Raft日誌複製 8

5.1. 什麼是日誌複製? 8

5.2. 日誌複製的實現 8

5.3. 腦裂時的複製 10

6. 概念對比 12

7. 共性探討 13

7.1. PacificA和HBase和Kafka 13

7.2. Redis&Raft&ZAB&PaxOS 13

8. 總結濃縮 13

8.1. 三種角色 13

8.2. 兩種RPC 13

8.3. 兩個超時時間 14

9. 參考 14

 

1. 前言

常見的一致性協議主要有:PaxOS、Raft、ZAB、PacificA等。同PaxOS,Raft也不考慮拜占庭將軍問題(Byzantine failures,注:比特幣採用工作量證明PoW和股權證明PoS解決了拜占庭將軍問題)。

2. 名詞

Distributed Consensus

分散式一致性

Distributed

Consensus Algorithms

分散式一致性演算法,PaxOS是分散式一致性演算法的鼻祖,絕大多數的實現要麼基於PaxOS,要麼受PaxOS影響(如Zookeeper)。

PaxOS

就分散式系統中的某個值(在PaxOS中叫決議,即Proposer)達成一致的演算法。PaxOS是分散式一致性協議的鼻祖,在Google的三大件未推出之前少為人知,1989年萊斯利·蘭伯特提出。Leslie Lamport是大神的英文名,出生於1941年2月7日紐約。蘭伯特目前就職於微軟研究院,他也是排版系統LaTeX的作者,2008年獲得馮諾依曼獎,2013年獲得圖靈獎,麻省理工學院學士和布蘭戴斯大學博士。最早的實現為Google內部使用的Chubby。

Raft

木筏協議,一種共識演算法,旨在替代PaxOS,相比容易理解許多。史丹佛大學的兩個博士Diego Ongaro和John Ousterhout兩個人以易懂為目標設計的一致性演算法,2013以論文方式釋出。由於易懂,不從論文釋出到不到兩年的時間,即出現大量的Raft開源實現。為簡化PaxOS的晦澀,它採用分而治之的辦法,將演算法分為三個子問題:選舉(Leader Election)、日誌複製(Log Replication)、安全性(Safety)和叢集成員動態變更(Cluster Membership Changes)。

ZAB

Zookeeper採用的一致性演算法,全稱“ZooKeeper Atomic Broadcast”,即Zookeeper原子廣播協議,實為Zookeeper版本的PaxOS。

PacificA

微軟2008年提出的日誌複製協議,與PaxOS和Raft不同,PacificA並不提供Leader選舉,因此需要結合PaxOS或Raft,分散式訊息佇列Kafka的ISR演算法類似於PacificA。小米開源的KV系統Pegasus基於PacificA實現。

Viewstamped Replication

簡稱VR,最初被提出是作為資料庫中的一部分工作,2012年作為單獨的分散式共識演算法再次發表。

Log Replication

日誌複製

Leader Election

領導選舉

Log

代表一個修改操作

Entry

(日誌)條目(代表一個修改操作),Log在Raft中的實體,即一個Entry表示一條Log

Term

任期(可簡單理解成租約,但稍有不同),是一個從0開始遞增的整數值,每次發起選舉時增一,最終所有節點的Term值是相同的。等同於ZAB協議中的zxid的高32位,即ZAB中的Epoch。

Index

和Term密切相關,Term針對的是選舉,而Index針對的是一條Log,它的值也是從0開始遞增。同樣最終所有節點的Index也相同,如果兩個節點的Term和Index均相同,則這兩個節點的資料是完全一致的。等同於ZAB協議中的zxid的低32位。

Node

節點

Single Node

單節點

Multiple Nodes

多節點

Leader

領導(唯一接受修改操作的)

Follower

跟隨者(投票參與者)

Candidate

候選人(唯一具有發起選舉的)

Vote

投票

Heal

恢復,比如恢復網路分割槽(Heal the network partition)

Step Down

下臺,當一個Leader發現有更大的Term時,自動降為Follower

Logic Clock

邏輯時鐘,由Lampert提出,原因是分散式中無法使用精準的時鐘維護事件的先後順序,方法是為每個訊息(或日誌)加上一個邏輯時間戳。在ZAB協議中,每個訊息均由唯一的zxid標識。zxid是一個64位無符號整數值,被分成兩部分:高32位是Epoch,低32位是一個從0開始的遞增值。類似於Raft的Term,每次選舉新Leader,Epoch值增一,但同時zxid值清0。而Raft也類似,實際也是兩部分組成,Term對應於Epoch,Index對應於zxid的低32位。

Quorum

法定人數

Brain Split

腦裂,同一個網路的節點被分成了兩個或多個不互通的部分

3. 什麼是分散式一致性?

多個節點(Multiple Nodes)的資料完全相同,即為分散式一致性。但因為多個節點通過網路互聯,並不一定時刻可用,而服務不能因為某些節點(特別是少數節點)不可用時,導致整個系統不可用。

Raft將節點分成三種角色:Leader、Follower和Candidate,一個節點可為三種角色中的任意一種,但同一時刻只會為其中一種角色。正常情況下,只有Leader和Follower兩種角色的節點,只有在選舉時才會出現Candidate角色節點,而且可能多個節點同時處於Candidate角色同時競爭選舉。

4. Raft選舉

4.1. 什麼是Leader選舉?

Raft協議中,一個節點有三個狀態:Leader、Follower和Candidate,但同一時刻只能處於其中一種狀態。Raft選舉實際是指選舉Leader,選舉是由候選者(Candidate)主動發起,而不是由其它第三者。

並且約束只有Leader才能接受寫和讀請求,只有Candidate才能發起選舉。如果一個Follower和它的Leader失聯(失聯時長超過一個Term),則它自動轉為Candidate,併發起選舉

發起選舉的目的是Candidate請求(Request)其它所有節點投票給自己,如果Candidate獲得多數節點(a majority of nodes)的投票(Votes),則自動成為Leader,這個過程即叫Leader選舉

在Raft協議中,正常情況下Leader會週期性(不能大於Term)的向所有節點傳送AppendEntries RPC,以維持它的Leader地位。

相應的,如果一個Follower在一個Term內沒有接收到Leader發來的AppendEntries RPC,則它在延遲隨機時間(150ms~300ms)後,即向所有其它節點發起選舉。

採取隨機時間的目的是避免多個Followers同時發起選舉,而同時發起選舉容易導致所有Candidates都未能獲得多數Followers的投票(腦裂,比如各獲得了一半的投票,誰也不佔多數,導致選舉無效需重選),因而延遲隨機時間可以提高一次選舉的成功性。

4.2. 選舉的實現

在Raft中,和選舉有關的超時值有兩個:

選舉超時(Election Timeout)

Follower等待成為Candidate的時間,為隨機時間,隨機範圍為150ms~250ms。如果在超時之前收到了其它的AppendEntries,則重置選舉超時重新計時,否則在選舉超時後,Follower成為Candidate,投票給自己(Votes for itself)併發起新的選舉任期(Term),傳送RequestVote給所有其它節點,以請求投票給自己。如果在這個Term時間內仍然沒有獲得投票,則重置選舉超時,以準備重新發起選舉。而一旦獲得多數節點的投票,則成為Leader。成為Leader後,定期性的傳送AppendEntries給所有Followers,以維持Leader地位。傳送AppendEntries的間隔叫作心跳超時

心跳超時(Heartbeat Timeout)

Follower會應答每一個AppendEntries,一個選舉任期(Election Term)會一直存在,直到有新的Follower成為Candidate。Leader節點通過定期傳送AppendEntries維護自己的Leader地位,定期的間隔時長即為心跳超時時長。在Raft中,心跳方向是:Leader -> Follower

 

如果一個Follower在一個Term時間內沒有收到AppendEntries(並非要求是來自Leader的AppendEntries,其它Candidates也一樣有效),則它成為Candidate,並在等待選舉超時(Election Timeout)後發起選舉。

要求獲得多數節點的投票,可以保證每個任期(Term)內只有一個Leader。如果兩個Candidates同時發起選舉,則會分割投票,這樣需要再重新發起選舉。Raft設計“選舉超時”,可以降低兩個或多個Candidates同時發起選舉的概率,減少連續重選舉。

Redis的選舉有個借鑑意義,在Redis叢集中,不同節點可有不同權重,權重大的優先獲得發起選舉機率。在Redis中,權重是指複製節點資料的新舊程度,而對Raft來說可考慮機架機房等作為權重。

4.3. Term和Lease比較

Raft中的Term可以簡單看成Lease(租約),但概念上仍然是有區別的。Lease通常是一個節點向一箇中心化節點請求,而Term是無中心化的。

如果沒有發生選舉超時,則Term的值不會發生變化,否則至少增一。

4.4. 選舉圖示

每個節點的Term值初始化為1(圓圈中的數字),每個節點選舉超時時間為一個150ms~300ms的隨機值(實際實現可以調整,但這個值是發明者測試得到的優值,詳情請參見兩位史丹佛大學教授的論文):

 

節點S2最先發起選舉成為Leader,在發起選舉之前,它需要先自增自己的Term值,因此Term值由1變成2,同時其它節點的Term值也需要更新為比自己大的Term值(如果一個Leader遇到一個更大的Term,它需要主動降為Follower):

 


人工將節點S1和S2停掉,這時兩者不可用。這會導致其它正常的節點S3、節點S4和節點S5在選舉超時時間內收不到來自Leader的心跳(實為AppendEntry),因此會觸發節點S3、節點S4和節點S5發起選舉(究竟哪個節點發起選舉,視哪個節點最先達到選舉超時):

因為節點S2的資料處於未提交狀態,因為它的已提交Index值要小於S3和S5,它沒有最新的資料,不可能成為新的Leader。新的Leader必然在S3和S5中產生。先強制重置S2的選舉超時時長,以讓出優先發起選舉:

雖然S3和S5均有最新的資料,但因為S3的Term小於S5的Term,因此只有S5才能成為Leader,這樣最新的Term值將為5+1為6:

這個時候恢復節點S1,Term值是否會改變?

一個Leader節點,如果遇到比自有更大的Term值,則主動放棄Leader身份,並使用原Term替代自己的Term,然後轉變為Candidate重新開始選舉。

非Leader節點也是一樣的,只要遇到比自己大的Term,都會使用更大的Term替代自己的Term。這樣能保證節點均能正常通訊時,Leader節點總是持有最大的Term。

4.5. 選舉總結

能成為Leader的條件:

1) 有最大的Term;

2) 如果Term相同,則有最大的Index;

3) 如果Term相同,Index也相同,就看誰最先發起;

4) 最先發起者也不一定成為Leader,還得看誰最先獲得多數選票。

 

一個Leader主動放棄Leader條件:

1) 遇到比自己更大的Term,這個時候會轉為Follower。

5. Raft日誌複製

5.1. 什麼是日誌複製?

Leader收到一個修改請求後,先將該請求記錄到本地日誌,這條日誌在Raft中叫作Enry,此時處於未提交狀態(Uncommitted),然後複製Entry給所有的Followers。當多數Followers節點確認已完成該Entry的寫入後,Leader本地提交該Entry,響應客戶端成功,這個過程叫日誌複製(Log Replication)。如果一個Entry不能複製到多數節點,則該Entry狀態一直為未提交,如果發生Leader轉換,有可能被覆蓋。

5.2. 日誌複製的實現

正常的複製不需要理解,主要看異常時的複製處理。讓節點S5成為Leader,然後停掉節點S2、S3和S4,然後客戶端再請求寫操作,但此時由於寫操作不能得到多數節點的確認,所以無法提交(不能應用到狀態機中):


先停掉節點S1和S5,再恢復節點S2、S3和S4,並讓節點S3成為Leader,然後客戶端發起一筆寫操作。讓寫操作在三個節點S2、S3和S4中得到確認,這樣該筆寫操作為已提交狀態(Committed),可以看到變成如下狀態了。顯然節點S1和節點S5上存在了兩筆髒資料:

恢復節點S1:

停止節點S3,構造條件讓S1成為新的Leader:

5.3. 腦裂時的複製

網路分裂時,出現兩個Leader:

 

具有多數節點的一方仍然能夠繼續,但少數一方已不可用,這個時候從節點B讀不到最新的資料:

 

當網路恢復後,節點B發現更大的Term,自動從Leader將為Follower,同時回滾未提交的Entries,叢集又重新實現一致。

6. 概念對比

Raft

PaxOS

(Multi PaxOS)

ZAB

PacificA

Viewstamped Replication

獲得多數同意

獲得多數同意

獲得多數同意

需所有都同意

獲得多數同意

強Leader(領導),總是包含了最全最新的日誌,日誌無空洞,隨機化簡化Leader選舉(Redis也採用了這個方法,並且加了節點權重)。

Proposer(提議者),允許有多個Proposer,並且不一定最包含了最全最新的日誌,日誌可存在空洞

強Leader(領導)

Primary(主)

Primary

Follower(跟隨者)

Acceptor(接受者)

Follower(跟隨者,參與投票)

Secondary(副),接收Primary的心跳,一個Primary對多個Secondary

Backup

Candidate(候選者)

Learner(學習者)

Looking(競選狀態)

 

 

 

 

Observing(觀察者,不參與投票,只同步狀態)

 

 

Entry

達到一致的叫Value(即決議),達到一致前叫提議

類似PaxOS

 

 

Term(任期)

Ballot(選票)

Epoch(紀元),為txid的高32位(注:Redis中也叫Epoch,作用類似

View

 

Index(序號)

Proposal Number

txid的低32位

Serial Number

 

7. 共性探討

所有的並不是完全孤立的,而是存在很多共性。

7.1. PacificA和HBase和Kafka

PacificA和HBase均依賴於PaxOS這樣的設施,HBase依賴於Zookeeper,PacificA同樣依賴於類似的配置管理服務。

如同HBase依賴Zookeeper來發現Keys的位置資訊,PacificA依賴配置管理服務發現分片的位置。

Kafka其實也類似,早期Kafka直接將位置資訊儲存在了Zookeeper中,遭遇了Zookeeper吞吐瓶頸,於是採取和HBase類似的做法,將位置資訊作為一個特殊的Partition。

7.2. Redis&Raft&ZAB&PaxOS

Redis同Raft、ZAB和PaxOS並不是一類東西,但核心均有Quorum的樸素思想,即服從多數(多數人理解的即為真理),而且均依賴於一個遞增的值來達到一致性狀態,這個遞增的值在Redis和ZAB中叫Epoch,Raft中叫Term。

8. 總結濃縮

8.1. 三種角色

1) Leader(領導者)

2) Follower(跟隨者)

3) Candidate(候選人)

8.2. 兩種RPC

1) AppendEntry(心跳和寫操作)

2) RequestVote(請求投票)

8.3. 兩個超時時間

中文名

英文名

主要問題

小技巧

選舉超時

Election Timeout

選票分裂造成活鎖

隨機時間(Redis非強一致,另加了權重)

心跳超時

Heartbeat Timeout

不能小於選舉超時時長

 

9. 參考

1) https://translate.google.cn

2) https://raft.github.io/raft.pdf

3) https://raft.github.io/

4) http://thesecretlivesofdata.com/raft/

5) PacificA:微軟設計的分散式儲存框架

6) 分散式共識(Consensus):Viewstamped Replication、Raft以及Paxos

7) https://static.googleusercontent.com/media/research.google.com/en//archive/paxos_made_live.pdf

 

相關文章