一致性算法Raft詳解
背景
熟悉或瞭解分佈性系統的開發者都知道一致性算法的重要性,Paxos一致性算法從90年提出到如今已經有二十幾年了,而Paxos流程太過於繁雜實現起來也比較複雜,可能也是覺得過於複雜 如今我據說過比較出名使用到Paxos的也就只是Chubby、libpaxos,搜了下發現Keyspace、BerkeleyDB數據庫中也使用了該算法做爲數據的一致性同步,雖然如今很普遍使用的Zookeeper也是基於Paxos算法來實現,可是Zookeeper使用的ZAB(Zookeeper Atomic Broadcast)協議對Paxos進行了不少的改進與優化,算法複雜我想會是制約他發展的一個重要緣由;說了這麼多隻是爲了要引出本篇文章的主角Raft一致性算法,沒錯Raft就是在這個背景下誕生的,文章開頭也說到了Paxos最大的問題就是複雜,Raft一致性算法就是比Paxos簡單又能實現Paxos所解決的問題的一致性算法。
Raft是斯坦福的Diego Ongaro、John Ousterhout兩我的以易懂(Understandability)爲目標設計的一致性算法,在2013年發佈了論文:《In Search of an Understandable Consensus Algorithm》從2013年發佈到如今不過只有兩年,到如今已經有了十多種語言的Raft算法實現框架,較爲出名的有etcd,Google的Kubernetes也是用了etcd做爲他的服務發現框架;因而可知易懂性是多麼的重要。算法
Raft概述
與Paxos不一樣Raft強調的是易懂(Understandability),Raft和Paxos同樣只要保證n/2+1節點正常就可以提供服務;衆所周知但問題較爲複雜時能夠把問題分解爲幾個小問題來處理,Raft也使用了分而治之的思想把算法流程分爲三個子問題:選舉(Leader election)、日誌複製(Log replication)、安全性(Safety)三個子問題;這裏先簡單介紹下Raft的流程;
Raft開始時在集羣中選舉出Leader負責日誌複製的管理,Leader接受來自客戶端的事務請求(日誌),並將它們複製給集羣的其餘節點,而後負責通知集羣中其餘節點提交日誌,Leader負責保證其餘節點與他的日誌同步,當Leader宕掉後集羣其餘節點會發起選舉選出新的Leader;數據庫
Raft簡介數組
Raft是一個用於日誌複製,同步的一致性算法。它提供了和Paxos同樣的功能和性能,可是它的算法結構與Paxos不一樣。這使得Raft相比Paxos更好理解,而且更容易構建實際的系統。爲了強調可理解性,Raft將一致性算法分解爲幾個關鍵流程(模塊),例如選主,安全性,日誌複製,經過將分佈式一致性這個複雜的問題轉化爲一系列的小問題進而各個擊破的方式來解決問題。同時它經過實施一個更強的一致性來減小一些沒必要要的狀態,進一步下降了複雜性。Raft還包括了一個新機制,容許線上進行動態的集羣擴容,利用有交集的大多數機制來保證安全性。安全
####一些背景知識網絡
#####A. 一致性算法簡單回顧框架
一致性算法容許一個集羣像一個總體同樣工做,即便其中一些機器出現故障也不會影響正常服務。正由於如此,一致性算法在構建可信賴的大型分佈式系統中都扮演着重要的角色。Paxos算法在過去10年中統治着這一領域:絕大多數實現都是基於Paxos,同時在教學領域講解一致性問題時Paxos也常常拿來做爲範例。可是不幸的是,儘管有不少工做都在試圖下降它的複雜性,可是Paxos仍然難以理解。而且,Paxos自身算法的結構不能直接用於實際系統中,想要使用必需要進行大幅度改變,這些都致使不管在工業界仍是教育界,Paxos都讓人很DT。分佈式
時勢造英雄,在這樣的背景下Raft應運而生。Raft算法使用了一些特別的技巧使得它易於理解,包括算法分解(Raft主要分爲選主,日誌複製和安全三個大模塊),同時在不影響功能的狀況下,減小複製狀態機的狀態,下降複雜性。Raft算法或多或少的和已經存在的一些一致性算法有着類似之處,可是也具備以下特徵:性能
- 強leader語義:相比其餘一致性算法,Raft使用加強形式的leader語義。舉個例子,日誌只能由leader複製給其它節點。這簡化了日誌複製須要的管理工做,使得Raft易於理解。
- leader的選擇:Raft使用隨機計時器來選擇leader,它的實現只是在心跳機制(任何一致性算法中都必須實現)上多作了一點「文章」,不會增長延遲和複雜性。
- 關係改變:Raft使用了一個新機制joint consensus容許集羣動態在線擴容,保障Raft的可持續服務能力。
Raft算法已經被證實是安全正確的,同時也有實驗支撐Raft的效率不比其餘一致性算法差。最關鍵是Raft算法要易於理解,在實際系統應用中易於實現的特色使得它成爲了解決分佈式系統一致性問題上新的「寵兒」。優化
#####B. 複製狀態機spa
一致性算法是從複製狀態機中提出的。簡單地講,複製狀態機就是經過彼此之間的通訊來將一個集羣從一個一致性狀態轉向下一個一致性狀態,它要能容忍不少錯誤情形。經典如GFS,HDFS都是使用了單獨的複製狀態機負責選主,存儲一些可以在leader crash狀況下進行恢復的配置信息等,好比Chubby和ZooKeeper。
複製狀態機的典型實現是基於複製日誌,以下圖所示:
每一個server上存儲了一個包含一系列指令的日誌,這些指令,狀態機要按序執行。每個日誌在相同的位置存放相同的指令(能夠理解成一個log包含一堆entry,這些entry組成一個數組,每一個entry是一個command,數組相同偏移處的command相同),因此每個狀態機都執行了相同序列的指令。一致性算法就是基於這樣一個簡單的前提:集羣中全部機器當前處於一個一致性狀態,若是它們從該狀態出發執行相同序列的指令走狀態機的話,那麼它們的下一個狀態必定一致。但因爲分佈式系統中存在三態(成功,失敗,無響應),如何來確保每臺機器上的日誌一致就很是複雜,也是一致性算法須要解決的問題。一般來說,一致性算法須要具備如下特徵:
- 安全性:在非拜占庭故障下,包括網絡分區,延遲,丟包,亂序等等狀況下都保證正確。
- 絕對可用:只要集羣中大多數機器正常,集羣就能錯誤容忍,進行正常服務。
- 不依賴時序保證一致性:因爲使用邏輯時鐘,因此物理時鐘錯誤或者極端的消息延遲都不影響可用性。
- 一般狀況下,一個指令能夠在一輪RPC週期內由大多數節點完成,宕機或者運行速度慢的少數派不影響系統總體性能。
####Raft算法
前面對Raft算法進行了簡要的介紹,這裏開始對它進行深刻分析。Raft實現一致性的機制是這樣的:首先選擇一個leader全權負責管理日誌複製,leader從客戶端接收log entries,將它們複製給集羣中的其它機器,而後負責告訴其它機器何時將日誌應用於它們的狀態機。舉個例子,leader能夠在無需詢問其它server的狀況下決定把新entries放在哪一個位置,數據永遠是從leader流向其它機器。一個leader能夠fail或者與其餘機器失去鏈接,這種情形下會有新的leader被選舉出來。
經過leader機制,Raft將一致性難題分解爲三個相對獨立的子問題:
- Leader選舉:當前leader跪了的狀況下,新leader被選舉出來。
- 日誌複製:leader必須可以從客戶端接收log entries,而後將它們複製給其餘機器,強制它們與本身一致。
- 安全性:若是任何節點將偏移x的log entry應用於本身的狀態機,那麼其餘節點改變狀態機時使用的偏移x的指令必須與之相同。
#####A. Raft基本知識
一個Raft集羣包含單數個server,5個是一個典型配置,容許該系統最多容忍兩個機器fail。在任什麼時候刻,每一個server有三種狀態:leader,follower,candidate。正常運行時,只有一個leader,其他全是follower。follower是被動的:它們不主動提出請求,只是響應leader和candidate的請求。leader負責處理全部客戶端請求(若是客戶端先鏈接某個follower,該follower要負責把它重定向到leader)。第三種狀態candidate用於選主。下圖展現了這些狀態以及它們之間的轉化:
Raft將時間分解成任意長度的terms,以下圖所示:
terms有連續單調遞增的編號,每一個term開始於選舉,這一階段每一個candidate都試圖成爲leader。若是一個candidate選舉成功,它就在該term剩餘週期內履行leader職責。在某種情形下,可能出現選票分散,沒有選出leader的狀況,這時新的term當即開始。Raft確保在任何term都只可能存在一個leader。term在Raft用做邏輯時鐘,servers能夠利用term判斷一些過期的信息:好比過期的leader。每臺server都存儲當前term號,它隨時間單調遞增。term號能夠在任何server通訊時改變:若是某臺server的當前term號小於其它servers,那麼這臺server必須更新它的term號,與它人保持一致;若是一個candidate或者leader發現本身的term過時,它就必需要「放下身段」變成follower;若是某臺server收到一個過期的請求(擁有過期的term號),它會拒絕該請求。Raft servers使用RPC交互,基本的一致性算法只須要兩種RPC。RequestVote RPCs由candidate在選舉階段發起。AppendEntries RPCs在leader複製數據時發起,leader在和其餘人作心跳時也用該RPC。servers發起一個RPC,若是在沒獲得響應,則須要不斷重試。另外,發起RPC是並行的。
#####B. leader選舉
Raft使用心跳機制來觸發選舉。當server啓動時,初始狀態都是follower。每個server都有一個定時器,超時時間爲election timeout,若是某server沒有超時的狀況下收到來自leader或者candidate的任何RPC,定時器重啓,若是超時,它就開始一次選舉。leader給其餘人發RPC要麼複製日誌,要麼就是用來告訴followers老子是leader,大家不用選舉的心跳(告訴followers對狀態機應用日誌的消息夾雜在心跳中)。若是某個candidate得到了大多數人的選票,它就贏得了選舉成爲新leader。每一個server在某個term週期內只能給最多一我的投票,按照先來先給的原則。新leader要給其餘人發送心跳,阻止新選舉。
在等待選票過程當中,一個candidate,假設爲A,可能收到它人的聲稱本身是leader的AppendEntries RPC,若是那傢伙的term號大於等於A的,那麼A認可他是leader,本身從新變成follower。若是那傢伙比本身小,那麼A拒絕該RPC,繼續保持candidate狀態。
還有第三種可能性就是candidate既沒選舉成功也沒選舉失敗:若是多個followers同時成爲candidate去拉選票,致使選票分散,任何candidate都沒拿到大多數選票,這種狀況下Raft使用超時機制來解決。Raft給每個server都分配一個隨機長度的election timeout(通常是150——300ms),因此同時出現多個candidate的可能性不大,即便機緣巧合同時出現了多個candidate致使選票分散,那麼它們就等待本身的election timeout超時,從新開始一次新選舉(再一再二不能再三再四,不可能每次都同時出現多個candidate),實驗也證實這個機制在選舉過程當中收斂速度很快。
#####C. 日誌複製
一旦選舉出了一個leader,它就開始負責服務客戶端的請求。每一個客戶端的請求都包含一個要被複制狀態機執行的指令。leader首先要把這個指令追加到log中造成一個新的entry,而後經過AppendEntries RPCs並行的把該entry發給其餘servers,其餘server若是發現沒問題,複製成功後會給leader一個表示成功的ACK,leader收到大多數ACK後應用該日誌,返回客戶端執行結果。若是followers crash或者丟包,leader會不斷重試AppendEntries RPC。Logs按照下圖組織:
每一個log entry都存儲着一條用於狀態機的指令,同時保存從leader收到該entry時的term號。該term號能夠用來判斷一些log之間的不一致狀態。每個entry還有一個index指明本身在log中的位置。
leader須要決定何時將日誌應用給狀態機是安全的,被應用的entry叫committed。Raft保證committed entries持久化,而且最終被其餘狀態機應用。一個log entry一旦複製給了大多數節點就成爲committed。同時要注意一種狀況,若是當前待提交entry以前有未提交的entry,即便是之前過期的leader建立的,只要知足已存儲在大多數節點上就一次性按順序都提交。leader要追蹤最新的committed的index,並在每次AppendEntries RPCs(包括心跳)都要捎帶,以使其餘server知道一個log entry是已提交的,從而在它們本地的狀態機上也應用。
Raft的日誌機制提供兩個保證,統稱爲Log Matching Property:
- 不一樣機器的日誌中若是有兩個entry有相同的偏移和term號,那麼它們存儲相同的指令。
- 若是不一樣機器上的日誌中有兩個相同偏移和term號的日誌,那麼日誌中這個entry以前的全部entry保持一致。
第一個保證是因爲一個leader在指定的偏移和指定的term,只能建立一個entry,log entries不能改變位置。第二個保證經過AppendEntries RPC的一個簡單的一致性檢查機制完成。當發起一個AppendEntries RPC,leader會包含正好排在新entries以前的那個entry的偏移和term號,若是follower發如今相同偏移處沒有相同term號的一個entry,那麼它拒絕接受新的entries。這個一致性檢查以一種相似概括法的方式進行:初始狀態你們都沒有日誌,不須要進行Log Matching Property檢查,可是不管什麼時候followers只要日誌要追加都要進行此項檢查。所以,只要AppendEntries返回成功,leader就知道這個follower的日誌必定和本身的徹底同樣。
在正常情形下,leader和follower的日誌確定是一致的,因此AppendEntries一致性檢查從不失敗。然而,若是leader crash,那麼它們的日誌極可能出現不一致。這種不一致會隨着leader或者followers的crash變得很是複雜。下圖展現了全部日誌不一致的情形:
如上圖(a)(b)followers可能丟失日誌,(c)(d)有多餘的日誌,或者是(e)(f)跨越多個terms的又丟失又多餘。在Raft裏,leader強制followers和本身的日誌嚴格一致,這意味着followers的日誌極可能被leader的新推送日誌所覆蓋。
leader爲了強制它人與本身一致,勢必要先找出本身和follower之間存在分歧的點,也就是個人日誌與大家的從哪裏開始不一樣。而後令followers刪掉那個分歧點以後的日誌,再將本身在那個點以後的日誌同步給followers。這個實現也是經過AppendEntries RPCs的一致性檢查來作的。leader會把發給每個follower的新日誌的偏移nextIndex也告訴followers。當新leader剛開始服務時,它把全部follower的nextIndex都初始化爲它最新的log entry的偏移+1(如上圖中的11)。若是一個follower的日誌和leader的不一致,AppendEntries RPC會失敗,leader就減少nextIndex而後重試,直到找到分歧點,剩下的就好辦了,移除衝突日誌entries,同步本身的。固然這裏有很大的優化餘地,徹底不須要一步一步回溯,怎麼玩請本身看論文 ,很簡單。
#####D. 安全性
前文講解了Raft如何選主和如何進行日誌複製,然而這些還不足以保證不一樣節點能執行嚴格一致的指令序列,須要額外的一些安全機制。好比,一個follower可能在當前leader commit日誌時不可用,然而過會它又被選舉成了新leader,這樣這個新leader可能會用新的entries覆蓋掉剛纔那些已經committed的entries。結果不一樣的複製狀態機可能會執行不一樣的指令序列,產生不一致的情況。這裏Raft增長了一個能夠確保新leader必定包含任何以前commited entries的選舉機制。
(1) 選舉限制
Raft使用了一個投票規則來阻止一個不包含全部commited entries的candidate選舉成功。一個candidate爲了選舉成功必須聯繫大多數節點,假設它們的集合叫A,而一個entry若是能commit必然存儲在大多數節點,這意味着對於每個已經committed的entry,A集合中必然有一個節點持有它。若是這個candidate的Log不比A中任何一個節點舊纔有機會被選舉爲leader,因此這個candidate若是要成爲leader必定已經持有了全部commited entries(注意我說的持有指只是儲存,不必定被應用到了複製狀態機中。好比一個老的leader將一個entry發往大多數節點,它們都成功接收,老leader隨即就commit這個entry,而後掛掉,此時這個entry叫commited entry,可是它不必定應用在了集羣中全部的複製狀態機上)。這個實如今RequestVote RPC中:該RPC包含了candidate log的信息,選民若是發現被選舉人的log沒有本身新,就拒絕投票。Raft經過比較最近的日誌的偏移和term號來決定誰的日誌更新。若是二者最近的日誌term號不一樣,那麼越大的越新,若是term號同樣,越長的日誌(擁有更多entries)越新。
(2) 提交早期terms的entries
正如前面所述的那樣,一個leader若是知道一個當前term的entry若是儲在大多數節點,就認爲它能夠commit。可是,一個leader不能也認爲一個早於當前term的entry若是存在大多數節點,那麼也是能夠commit的。下圖展現了這樣一種情況,一個老的log存儲在大多數節點上,可是仍有可能被新leader覆蓋掉:
要消除上圖中的問題,Raft採起針對老term的日誌entries毫不能僅僅經過它在集羣中副本的數量知足大多數,就認爲是能夠commit的。完整的commit語義也演變成:一個日誌entry若是可以被認爲是能夠提交的,必須同時知足兩個條件:
- 這個entry存儲在大多數節點上
- 當前term至少有一個entry存儲在大多數節點。
以上圖爲例,這兩個條件確保一旦當前leader將term4的entry複製給大多數節點,那麼S5不可能被選舉爲新leader了(日誌term號過期)。綜合考慮,經過上述的選舉和commit機制,leaders永遠不會覆蓋已提交entries,而且leader的日誌永遠絕對是」the truth」。
(3) 調解過時leader
在Raft中有可能同一時刻不僅一個server是leader。一個leader忽然與集羣中其餘servers失去鏈接,致使新leader被選出,這時剛纔的老leader又恢復鏈接,此時集羣中就有了兩個leader。這個老leader極可能繼續爲客戶端服務,試圖去複製entries給集羣中的其它servers。可是Raft的term機制粉碎了這個老leader試圖形成任何不一致的行爲。每個RPC servers都要交換它們的當前term號,新leader一旦被選舉出來,確定有一個大多數羣體包含了最新的term號,老leader的RPC都必需要聯繫一個大多數羣體,它必然會發現本身的term號過時,從而主動讓賢,退變爲follower狀態。
然而有多是老leader commit一個entry後失去鏈接,這時新leader必然有那個commit的entry,只是新leader可能還沒commit它,這時新leader會在初次服務客戶端前先把這個entry再commit一次,followers若是已經commit過直接返回成功,沒commit就commit後返回成功而已,不會形成不一致。
#####E. Follower 或者candidate crash
Followers和candidate的crash比起leader來講處理要簡單不少,它們的處理流程是相同的,若是某個follower或者candidate crash了,那麼將來發往它的RequestVote和AppendEntries RPCs 都會失敗,Raft採起的策略就是不停的重發,若是crash的機器恢復就會執行成功。另外,若是server crash是在完成一個RPC但在回覆以前,那麼在它恢復以後仍然會收到相同的RPC(讓它重試一次),Raft的全部RPC都是冪等操做,若是follower已經有了某個entry,可是leader又讓它複製,follower直接忽略便可。
#####F. 時間與可用性
Raft集羣的可用性須要知足一個時間關係,即下面的公式:
broadcastTime表明平均的廣播延遲(從並行發起RPC開始,直到收到大多數的回覆爲止)。electionTimeout就是前文所述的超時時間間隔,MTBF表明一個機器兩次宕機時間間隔的平均值。broadcastTime必須遠遠小於electionTimeout,不然會頻繁觸發無心義的選舉重啓。electionTimeout遠遠小於MTBF就很好理解了。
####集羣擴容
迄今爲止的討論都是假設集羣配置不變的狀況下(參與一致性算法的機器數目不變),可是實際中集羣擴容(廣義概念,泛指集羣容量變化,可增可減)有時是必須的。好比業務所需,集羣須要擴容,或者某機器永久損壞,須要從集羣中剔除等等。
擴容最大的挑戰就是保證一致性很困難,由於擴容不是原子的,有可能集羣中一部分機器用老配置信息,另外一部分用新配置信息,如上圖所示,所以究竟多少算大多數在集羣中可能存在分歧。擴容通常都分爲兩個階段,有不少實現方法,比較典型的就是第一階段,使全部舊配置信息失效,這段時間不對外提供服務;第二階段容許新配置信息,從新對外服務。在Raft中集羣第一階段首先使集羣轉向一個被稱做聯合一致性(joint consensus)的狀態;一旦這個聯合一致性(有一個特殊的Cold,new entry表徵)被提交,這個系統就開始向新配置遷移。joint consensus既包含老配置信息,又包含新配置信息,它的規則(Cold,new規則)以下:
- log entries複製給集羣中全部機器,無論配置信息新舊
- 任何配置的server都有權利當leader
- 達成一致所須要的大多數(無論是選舉仍是entry提交),既要包括老配置中的一個大多數也要包括新配置機器中的一個大多數。
joint consensus容許集羣配置平滑過渡而不失去安全和一致性,同時集羣可正常對外服務。集羣配置信息以一個特殊的entry表徵,存儲在log中並和其餘entry語義同樣。當一個leader收到擴容請求,它就建立一個Cold,new的entry,並複製給集羣其它其它機器,一旦有某個follower收到此entry並追加到本身的日誌中,它就使用Cold,new規則(不須要committed)。leader按照Cold,new 規則對Cold,new進行commit。若是一個leader crash了,新leader既多是沒收到Cold,new的又多是收到的,可是這兩種狀況都不會出現leader用Cnew規則作決策。
一旦Cold,new被提交,就開始向新配置轉換。Raft保證只有擁有Cold,new的節點纔會被選舉爲leader。如今leader能夠很安全地建立一個叫Cnew的entry,而後複製給集羣中屬於新配置的節點。配置信息仍是節點一收到就能夠用來作決策。Cnew的commit使用Cnew 規則,舊配置已經與集羣無關,新配置中不存在的機器能夠着手關閉,Raft經過保證不讓Cold和Cnew的節點有機會同時作決策來保障安全性。配置改變示意圖以下:
有三個要注意的點:
(1) 新server加入進集羣時也許沒有存儲任何log entries,它們須要很長時間來追上原有機器的「進度」,爲了防止集羣性能出現巨大的抖動,Raft在配置改變前又引入了一個特殊的階段,當新機器加入集羣時,它們做爲無投票權的節點,即leader仍是會將log entries複製給它們,可是不會被當作大多數對待。一旦這些「新人」追上了其餘機器的進度,動態擴容能夠像上述同樣執行。
(2) 當前leader也許不是集羣新配置中的一份子,在這種狀況下,一旦Cnew被提交,這個leader就主動退出。這意味着當Cnew提交過程當中這個leader要管理一個不包括它本身的集羣。這時,它在給其餘節點複製log entries時不把本身計入大多數。當Cnew提交成功,這個leader的使命就光榮完成了,在這個時間點前,只有Cold的節點纔會被選舉爲leader。
(3) 第三個問題是被移除的機器可能會影響集羣服務。這些機器不會再接收到心跳信息,它們所以可能重啓選舉,結果是它們用一個更新的term號發起RequestVote RPCs,當前leader無奈的退化爲follower。新leader(擁有新配置的)仍是會被選舉出來,可是那些被移除的機器仍是繼續超時,開啓選舉,致使系統可用性下降。爲解決這一問題,須要有一個機制讓集羣中節點可以忽略這種RequestVote RPCs,相信當前leader正在正常工做。Raft使用最小超時延遲(前面說過,Raft會隨機爲每一個節點指定一個隨機的election timeout,典型的是150ms到300ms,最小超時延遲就是它們之間的最小值)來保證,若是一個節點在最小超時延遲前收到一個RequestVote RPCs,那麼該節點不會提高term號,也不會爲其投票。這個不會影響正常的選舉,由於正常選舉中都是至少等待一個最小超時延遲纔開始下一次選舉的。
####Raft新集羣擴容方案
爲了下降集羣擴容的複雜性,Raft還有一個能夠備選的方案。它加強了擴容的限制:一次只容許一臺機器加入或移除集羣。複雜的擴容能夠經過一系列的單機增長或刪除變向實現。
當從集羣中添加或刪除一臺機器時,任何舊配置集羣中的大多數和任何新配置集羣中的大多數必然存在一個交集,以下圖所示。
注意就是這個交集讓集羣不可能存在兩個互相獨立,沒有交集的大多數羣體。所以系統中不會同時存在兩個leader(一個由舊配置集羣選舉出來,一個由新配置集羣選舉出來),所以擴容是安全的,集羣能夠直接從舊配置轉向新配置。
當leader收到添加或刪除一臺機器的請求,它將該Cnew添加到本身的log中,而後使用同樣的複製機制,把這個entry複製給屬於新配置集羣的全部節點。只要節點把Cnew添加進log,就立刻使用新配置信息,無需等到該entry提交。leader使用Cnew規則對Cnew進行提交,一旦提交完成,擴容就宣告完成,leader就知道任何沒有Cnew的節點都不可能造成一個大多數,這樣的節點也不可能成爲集羣的leader。Cnew的提交暗示瞭如下三件事情:
- leader獲知擴容成功完成
- 若是新配置移除一臺機器,那麼這臺機器能夠着手關閉
- 新的擴容能夠開始
正如上面所講的那樣,servers永遠使用log中最新的配置,無論這個配置entry是否被提交。這樣leader知道一