一致性問題可以算是分布式領(lǐng)域的一個圣殿級問題了物赶,關(guān)于它的研究可以回溯到幾十年前。
拜占庭將軍問題
Leslie Lamport 在三十多年前發(fā)表的論文《拜占庭將軍問題》(參考[1])七问。
拜占庭位于如今的土耳其的伊斯坦布爾,是東羅馬帝國的首都。由于當時拜占庭羅馬帝國國土遼闊嗓蘑,為了防御目的纸俭,因此每個軍隊都分隔很遠吧趣,將軍與將軍之間只能靠信差傳消息和悦。在戰(zhàn)爭的時候,拜占庭軍隊內(nèi)所有將軍必需達成 一致的共識榜揖,決定是否有贏的機會才去攻打敵人的陣營浑厚。但是股耽,在軍隊內(nèi)有可能存有叛徒和敵軍的間諜,左右將軍們的決定又擾亂整體軍隊的秩序钳幅,在進行共識時物蝙,結(jié)果并不代表大多數(shù)人的意見。這時候敢艰,在已知有成員不可靠的情況下诬乞,其余忠誠的將軍在不受叛徒或間諜的影響下如何達成一致的協(xié)議,拜占庭問題就此形成钠导。拜占庭假設(shè)是對現(xiàn)實世界的模型化震嫉,由于硬件錯誤、網(wǎng)絡(luò)擁塞或斷開以及遭到惡意攻擊牡属,計算機和網(wǎng)絡(luò)可能出現(xiàn)不可預(yù)料的行為票堵。
Lamport 一直研究這類問題,發(fā)表了一系列論文逮栅。但綜合總結(jié)一下就是回答下面三個問題:
- 類似拜占庭將軍這樣的分布式一致性問題是否有解悴势?
- 如果有解的話需要滿足什么樣的條件?
- 在特定前提條件的基礎(chǔ)上措伐,提出一種解法特纤。
前兩個問題 Lamport 在論文《拜占庭將軍問題》已經(jīng)回答,而第三個問題在后來的論文 《The Part-Time Parliament》中提出了一種算法并命名為 Paxos侥加。這篇論文使用了大量的數(shù)學(xué)證明捧存,而我基本就看不懂了(數(shù)學(xué)符號都認不全-?-;),考慮到大家理解起來都比較困難担败,后來 Lamport 又寫了另外一篇論文 《Paxos Made Simple》完全放棄了所有數(shù)學(xué)符號的證明昔穴,使用純英文的邏輯推導(dǎo)。我勉強逐字看了一遍提前,然后感覺若有所悟吗货,但你問我搞懂了嗎,我的標準應(yīng)該還是沒懂岖研。對我來說理解一個算法有個明確的標準,就是真的懂了會在頭腦里能將算法映射為代碼警检,而看完后面一篇論文僅僅是若有所悟還達不到能映射為代碼的清晰度孙援。
雖然 Lamport 認為 Paxos 很 simple,但也許只是針對他的頭腦而言扇雕。事實是大家理解起來都還是很困難拓售,所以 Raft 就是建立在希望得到一個更易于理解的 Paxos 算法的替代品。把可理解性作為算法的主要目標之一镶奉,從論文題目就可看出來《In Search of an Understandable Consensus Algorithm》础淤。
在進入正題前崭放,我想起一個舊故事可以很直觀的感受對一個問題不同的思維視角在可理解性上的差異。
不同視角的可理解性
依稀記得大約在二十年前鸽凶,我還在讀初中時在一本可能大概叫《數(shù)學(xué)中的發(fā)散思維》(不能很清晰記得書名了)的書中看到這么一個有趣的問題币砂。
甲乙兩人輪流在一張圓桌上平放黑白圍棋子,每次放一子玻侥,棋子不許重疊决摧,誰先沒有地方放就輸。
請問怎樣放才能贏凑兰?
這個問題有兩層意思掌桩,第一,有沒有一種放法保證必贏姑食?第二波岛,如果有怎么證明?這里先停頓下音半,思考十秒鐘则拷。
上面的圖回答了這個問題,就是先行者必勝祟剔,這里使用了三種不同的思維方式隔躲。
- 假如桌子只有一個圍棋子那么大。
- 假如桌子無限大物延,先行者先占住圓心宣旱,由于圓是對稱圖形,所以只要對手還能找到位置放叛薯,你總能在對稱的另一面找到位置放浑吟。
- 一個圓中可畫單數(shù)個直徑相等且互切的小圓。
三種不同的思維方式在可理解性難度上逐漸加深耗溜。第一種是極簡化思維组力,但數(shù)學(xué)上是不嚴謹?shù)摹5诙N是極限思維抖拴,和第一種結(jié)合起來就是數(shù)學(xué)歸納法了燎字,在數(shù)學(xué)上是嚴謹?shù)摹5谌N是形象思維阿宅,使用了幾何學(xué)概念候衍,但對于沒有幾何學(xué)基礎(chǔ)知識的人就很難理解了。
Raft 協(xié)議的易理解性描述
雖然 Raft 的論文比 Paxos 簡單版論文還容易讀了洒放,但論文依然發(fā)散的比較多蛉鹿,相對冗長。讀完后掩卷沉思覺得還是整理一下才會更牢靠往湿,變成真正屬于自己的妖异。這里我就借助前面黑白棋落子里第一種極簡思維來描述和概念驗證下 Raft 協(xié)議的工作方式惋戏。
在一個由 Raft 協(xié)議組織的集群中有三類角色:
- Leader(領(lǐng)袖)
- Follower(群眾)
- Candidate(候選人)
就像一個民主社會,領(lǐng)袖由民眾投票選出他膳。剛開始沒有領(lǐng)袖响逢,所有集群中的參與者都是群眾,那么首先開啟一輪大選矩乐,在大選期間所有群眾都能參與競選龄句,這時所有群眾的角色就變成了候選人,民主投票選出領(lǐng)袖后就開始了這屆領(lǐng)袖的任期散罕,然后選舉結(jié)束分歇,所有除領(lǐng)袖的候選人又變回群眾角色服從領(lǐng)袖領(lǐng)導(dǎo)。這里提到一個概念「任期」欧漱,用術(shù)語 Term 表達职抡。關(guān)于 Raft 協(xié)議的核心概念和術(shù)語就這么多而且和現(xiàn)實民主制度非常匹配,所以很容易理解误甚。三類角色的變遷圖如下缚甩,結(jié)合后面的選舉過程來看很容易理解。
Leader 選舉過程
在極簡的思維下窑邦,一個最小的 Raft 民主集群需要三個參與者(如下圖:A擅威、B、C)冈钦,這樣才可能投出多數(shù)票郊丛。初始狀態(tài) ABC 都是 Follower,然后發(fā)起選舉這時有三種可能情形發(fā)生瞧筛。下圖中前二種都能選出 Leader厉熟,第三種則表明本輪投票無效(Split Votes),每方都投給了自己较幌,結(jié)果沒有任何一方獲得多數(shù)票揍瑟。之后每個參與方隨機休息一陣(Election Timeout)重新發(fā)起投票直到一方獲得多數(shù)票。這里的關(guān)鍵就是隨機 timeout乍炉,最先從 timeout 中恢復(fù)發(fā)起投票的一方向還在 timeout 中的另外兩方請求投票绢片,這時它們就只能投給對方了,很快達成一致岛琼。
選出 Leader 后底循,Leader 通過定期向所有 Follower 發(fā)送心跳信息維持其統(tǒng)治。若 Follower 一段時間未收到 Leader 的心跳則認為 Leader 可能已經(jīng)掛了再次發(fā)起選主過程衷恭。
Leader 節(jié)點對一致性的影響
Raft 協(xié)議強依賴 Leader 節(jié)點的可用性來確保集群數(shù)據(jù)的一致性此叠。數(shù)據(jù)的流向只能從 Leader 節(jié)點向 Follower 節(jié)點轉(zhuǎn)移纯续。當 Client 向集群 Leader 節(jié)點提交數(shù)據(jù)后随珠,Leader 節(jié)點接收到的數(shù)據(jù)處于未提交狀態(tài)(Uncommitted)灭袁,接著 Leader 節(jié)點會并發(fā)向所有 Follower 節(jié)點復(fù)制數(shù)據(jù)并等待接收響應(yīng),確保至少集群中超過半數(shù)節(jié)點已接收到數(shù)據(jù)后再向 Client 確認數(shù)據(jù)已接收窗看。一旦向 Client 發(fā)出數(shù)據(jù)接收 Ack 響應(yīng)后茸歧,表明此時數(shù)據(jù)狀態(tài)進入已提交(Committed),Leader 節(jié)點再向 Follower 節(jié)點發(fā)通知告知該數(shù)據(jù)狀態(tài)已提交显沈。
在這個過程中软瞎,主節(jié)點可能在任意階段掛掉,看下 Raft 協(xié)議如何針對不同階段保障數(shù)據(jù)一致性的拉讯。
1. 數(shù)據(jù)到達 Leader 節(jié)點前
這個階段 Leader 掛掉不影響一致性涤浇,不多說。
2. 數(shù)據(jù)到達 Leader 節(jié)點魔慷,但未復(fù)制到 Follower 節(jié)點
這個階段 Leader 掛掉只锭,數(shù)據(jù)屬于未提交狀態(tài),Client 不會收到 Ack 會認為超時失敗可安全發(fā)起重試院尔。Follower 節(jié)點上沒有該數(shù)據(jù)蜻展,重新選主后 Client 重試重新提交可成功。原來的 Leader 節(jié)點恢復(fù)后作為 Follower 加入集群重新從當前任期的新 Leader 處同步數(shù)據(jù)邀摆,強制保持和 Leader 數(shù)據(jù)一致纵顾。
3. 數(shù)據(jù)到達 Leader 節(jié)點,成功復(fù)制到 Follower 所有節(jié)點栋盹,但還未向 Leader 響應(yīng)接收
這個階段 Leader 掛掉施逾,雖然數(shù)據(jù)在 Follower 節(jié)點處于未提交狀態(tài)(Uncommitted)但保持一致,重新選出 Leader 后可完成數(shù)據(jù)提交贞盯,此時 Client 由于不知到底提交成功沒有音念,可重試提交。針對這種情況 Raft 要求 RPC 請求實現(xiàn)冪等性躏敢,也就是要實現(xiàn)內(nèi)部去重機制闷愤。
4. 數(shù)據(jù)到達 Leader 節(jié)點,成功復(fù)制到 Follower 部分節(jié)點件余,但還未向 Leader 響應(yīng)接收
這個階段 Leader 掛掉讥脐,數(shù)據(jù)在 Follower 節(jié)點處于未提交狀態(tài)(Uncommitted)且不一致,Raft 協(xié)議要求投票只能投給擁有最新數(shù)據(jù)的節(jié)點啼器。所以擁有最新數(shù)據(jù)的節(jié)點會被選為 Leader 再強制同步數(shù)據(jù)到 Follower旬渠,數(shù)據(jù)不會丟失并最終一致。
5. 數(shù)據(jù)到達 Leader 節(jié)點端壳,成功復(fù)制到 Follower 所有或多數(shù)節(jié)點告丢,數(shù)據(jù)在 Leader 處于已提交狀態(tài),但在 Follower 處于未提交狀態(tài)
這個階段 Leader 掛掉损谦,重新選出新 Leader 后的處理流程和階段 3 一樣岖免。
6. 數(shù)據(jù)到達 Leader 節(jié)點岳颇,成功復(fù)制到 Follower 所有或多數(shù)節(jié)點,數(shù)據(jù)在所有節(jié)點都處于已提交狀態(tài)颅湘,但還未響應(yīng) Client
這個階段 Leader 掛掉话侧,Cluster 內(nèi)部數(shù)據(jù)其實已經(jīng)是一致的,Client 重復(fù)重試基于冪等策略對一致性無影響闯参。
7. 網(wǎng)絡(luò)分區(qū)導(dǎo)致的腦裂情況瞻鹏,出現(xiàn)雙 Leader
網(wǎng)絡(luò)分區(qū)將原先的 Leader 節(jié)點和 Follower 節(jié)點分隔開,F(xiàn)ollower 收不到 Leader 的心跳將發(fā)起選舉產(chǎn)生新的 Leader鹿寨。這時就產(chǎn)生了雙 Leader新博,原先的 Leader 獨自在一個區(qū),向它提交數(shù)據(jù)不可能復(fù)制到多數(shù)節(jié)點所以永遠提交不成功脚草。向新的 Leader 提交數(shù)據(jù)可以提交成功叭披,網(wǎng)絡(luò)恢復(fù)后舊的 Leader 發(fā)現(xiàn)集群中有更新任期(Term)的新 Leader 則自動降級為 Follower 并從新 Leader 處同步數(shù)據(jù)達成集群數(shù)據(jù)一致。
綜上窮舉分析了最小集群(3 節(jié)點)面臨的所有情況玩讳,可以看出 Raft 協(xié)議都能很好的應(yīng)對一致性問題涩蜘,并且很容易理解。
總結(jié)
就引用 Raft 論文最后的一節(jié)的綜述來總結(jié)本文吧熏纯。
算法以正確性同诫、高效性、簡潔性作為主要設(shè)計目標樟澜。
雖然這些都是很有價值的目標误窖,但這些目標都不會達成直到開發(fā)者寫出一個可用的實現(xiàn)。
所以我們相信可理解性同樣重要秩贰。
深以為然霹俺,想想 Paxos 算法是 Leslie Lamport 在 1990 年就公開發(fā)表在了自己的網(wǎng)站上,想想我們是什么時候才聽說的毒费?什么時候才有一個可用的實現(xiàn)丙唧?而 Raft 算法是 2013 年發(fā)表的,大家在參考[5]上面可以看到有多少個不同語言開源的實現(xiàn)庫了觅玻,這就是可理解性的重要性想际。