概述
《分佈式系統概念與設計》一書中對分佈式系統概念的定義以下:分佈式系統是一個硬件或軟件組件分佈在不一樣的網絡計算機上,彼此之間僅僅經過消息傳遞進行通訊和協調的系統html
分佈式系統的設計目標通常包括以下幾個方面git
- 可用性:可用性是分佈式系統的核心需求,其用於衡量一個分佈式系統持續對外提供服務的能力。
- 可擴展性:增長機器後不會改變或極少改變系統行爲,而且能得到近似線性的性能提高。
- 容錯性:系統發生錯誤時,具備對錯誤進行規避以及從錯誤中恢復的能力。
- 性能:對外服務的響應延時和吞吐率要能知足用戶的需求。
分佈式架構比單體式架構擁有更多的挑戰程序員
- 節點之間的網絡通訊是不可靠的,存在網絡延時和丟包等狀況。
- 存在節點處理錯誤的狀況,節點自身隨時也有宕機的可能。
- 同步調用使系統變得不具有可擴展性。
CAP原理
提到分佈式系統,就不得不提CAP原理。github
CAP的完整定義爲:算法
- C:Consistency(一致性)。這裏的一致性特指強一致,通俗地說,就是全部節點上的數據時刻保持同步。一致性嚴謹的表述是原子讀寫,即全部讀寫都應該看起來是「原子」的,或串行的。全部的讀寫請求都好像是經全局排序過的同樣,寫後面的讀必定能讀到前面所寫的內容。
- A:Availability(可用性)。任何非故障節點都應該在有限的時間內給出請求的響應,不論請求是否成功。
- P:Tolerance to the partition of network(分區容忍性)。當發生網絡分區時(即節點之間沒法通訊),在丟失任意多消息的狀況下,系統仍然可以正常工做。
CAP原理具備重大的指導意義:在任何分佈式系統中,可用性、一致性和分區容忍性這三個方面都是相互矛盾的,三者不可兼得,最多隻能取其二。數據庫
直觀的說明以下:
緩存
1)AP知足但C不知足:若是既要求系統高可用又要求分區容錯,那麼就要放棄一致性了。由於一旦發生網絡分區(P),節點之間將沒法通訊,爲了知足高可用(A),每一個節點只能用本地數據提供服務,這樣就會致使數據的不一致(!C)。一些信奉BASE(Basic Availability, Soft state, Eventually Consistency)原則的NoSQL數據庫(例如,Cassandra、CouchDB等)每每會放寬對一致性的要求(知足最終一致性便可),以此來換取基本的可用性。安全
2)CP知足但A不知足:若是要求數據在各個服務器上是強一致的(C),然而網絡分區(P)會致使同步時間無限延長,那麼如此一來可用性就得不到保障了(!A)。堅持事務ACID(原子性、一致性、隔離性和持久性)的傳統數據庫以及對結果一致性很是敏感的應用(例如,金融業務)一般會作出這樣的選擇。性能優化
3)CA知足但P不知足:指的是若是不存在網絡分區,那麼強一致性和可用性是能夠同時知足的。服務器
正如熱力學第二定律揭示了任未嘗試發明永動機的努力都是徒勞的同樣,CAP原理明確指出了完美知足CAP三種屬性的分佈式系統是不存在的。
瞭解CAP原理的目的在於,其可以幫助咱們更好地理解實際分佈式協議實現過程當中的取捨。
一致性
分佈式存儲系統一般會經過維護多個副原本進行容錯,以提升系統的可用性。這就引出了分佈式存儲系統的核心問題——如何保證多個副本的一致性?
「一致性」有三種含義:
-
Coherence這個單詞只在Cache Coherence場景下出現過,其所關注的是多核共享內存的CPU架構下,各個核的Cache上的數據應如何保持一致。
-
Consensus是共識,它強調的是多個提議者就某件事情達成共識,其所關注的是達成共識的過程,例如Paxos協議、Raft選舉等。Consensus屬於replication protocol的範疇。
-
Consistency表達的含義相對複雜一些,廣義上說,它描述了系統自己的不變量的維護程度對上層業務客戶端的影響,以及該系統的併發狀態會向客戶端暴露什麼樣的異常行爲。CAP、ACID中的C都有這層意思。
這裏重點討論的分佈式系統中的一致性問題,屬於上文中提到的Consensus和Consistency範疇。
分佈式系統的一致性是一個具有容錯能力的分佈式系統須要解決的基本問題。通俗地講,一致性就是不一樣的副本服務器承認同一份數據。一旦這些服務器對某份數據達成了一致,那麼該決定即是最終的決定,且將來也沒法推翻。
這裏有一點須要注意:一致性與結果的正確性沒有關係,而是系統對外呈現的狀態是否一致(統一)。例如,全部節點都達成一個錯誤的共識也是一致性的一種表現。
一致性協議就是用來解決一致性問題的,它能使得一組機器像一個總體同樣工做,即便其中的一些機器發生了錯誤也能正常工做。正由於如此,一致性協議在大規模分佈式系統中扮演着關鍵角色。
一致性協議從20世紀80年代開始研究,一致性協議衍生出了不少算法。衡量一致性算法的標準具體以下:
- 可終止性:非失敗進程在有限的時間內可以作出決定,等價於liveness。
- 一致性:全部的進程必須對最終的決定達成一致,等價於safety。
- 合法性:算法作出的決定值必須在其餘進程(客戶端)的指望值範圍以內。即客戶端請求回答「是」或「否」時,不能返回「不肯定」。
一致性模型
在給定了與操做和狀態相關的一些規則的狀況下,系統中的操做歷史應該老是遵循這些規則。咱們稱這些規則爲一致性模型
一致性模型分述
對於一致性,能夠分別從客戶端和服務端兩個不一樣的視角來理解。
從客戶端來看,一致性主要是指多併發訪問時如何獲取更新過的數據的問題。
從服務端來看,則是更新如何複製分佈到整個系統,以保證數據最終的一致性。
所以,能夠從兩個角度來查看一致性模型:以數據爲中心的一致性模型和以用戶爲中心的一致性模型。
以數據爲中心的一致性模型
實現如下這幾種一致性模型的難度會依次遞減,對一致性強度的要求也依次遞減。
-
嚴格一致性(Strong Consistency)
嚴格一致性也稱強一致性,原子一致性或者是可線性化(Linearizability),是要求最高的一致性模型。嚴格一致性的要求具體以下:
- 任何一次讀都能讀到某個數據的最近一次寫的數據。
- 系統中的全部進程,看到的操做順序,都與全局時鐘下的順序一致。
**嚴格一致性維護的是一個絕對全局時間順序。**單機系統遵照嚴格一致性,但對於分佈式系統,爲每一個操做都分配一個準確的全局時間戳是不可能實現的,因此嚴格一致性只是存在於理論中的一致性模型。
-
順序一致性(Sequential Consistency)
順序一致性,也稱爲可序列化,比嚴格一致性要求弱一點,但也是可以實現的最高級別的一致性模型。
由於全局時鐘致使嚴格一致性很難實現,所以順序一致性放棄了全局時鐘的約束,改成分佈式邏輯時鐘實現。順序一致性是指全部的進程都以相同的順序看到全部的修改。讀操做未必可以及時獲得此前其餘進程對同一數據的寫更新,可是每一個進程讀到的該數據不一樣值的順序倒是一致的。
知足順序一致性的存儲系統須要一個額外的邏輯時鐘服務。
下圖解釋了嚴格一致性和順序一致性
a) 順序一致性,從這兩個進程的角度來看,順序應該是這樣的:Write(y, 2)→Read(x, 0)→Write(x, 4)→Read(y, 2),徹底沒有衝突
b) 嚴格一致性,從兩個進程看到的操做順序與全局時鐘的順序同樣,都是Write(y, 2)→Write(x, 4)→Read(x, 4)→Read(y, 2)。
c) 不知足順序一致性,Write(x, 4)→Read(y, 0)→Write(y, 2)→Read(x, 0),這個有衝突
3. 因果一致性(Causal Consistency)
因果關係能夠描述成以下狀況:
- 本地順序:本進程中,事件執行的順序即爲本地因果順序。
- 異地順序:若是讀操做返回的是寫操做的值,那麼該寫操做在順序上必定在讀操做以前。
- 閉包傳遞:與時鐘向量裏面定義的同樣,若是a→b且b→c,那麼確定也有a→c。
不嚴格地說,因果一致性弱於順序一致性。
因果一致性和順序一致性的對比
因果關係可能比較難以理解,下面給你們解釋一下
P2寫x=7,P2同步到P3,P3讀到7
P1寫x=2,P1同步到P3,P3讀到2
P1寫x=4,P1同步到P4,P4讀到4
P2同步到P4,P4讀到7
永遠不會出現先讀到4,再讀到2的狀況
因果關係只保證因果的順序是正確的,其餘的順序不理會
-
可串行化一致性(Serializable Consis-tency)
若是說操做的歷史等同於以某種單一原子順序發生的歷史,但對調用和完成時間沒有說明,那麼就能夠得到稱爲可序列化的一致性模型。
在一個可序列化的系統中,有以下所示的這樣一個程序:
x = 1
x = x + 1
puts x
在這裏,咱們假設每行表明一個操做,而且全部的操做都成功。由於這些操做能夠以任何順序進行,因此可能打印出nil、1或2。所以,一致性顯得很弱。
但在另外一方面,串行化的一致性又很強,由於它須要一個線性順序。例如,下面的這個程序:
print x if x = 3
x = 1 if x = nil
x = 2 if x = 1
x = 3 if x = 2
它可能不會嚴格地以咱們編寫的順序發生,但它可以可靠地將x從nil→1→2,更改成3,最後打印出3。
以用戶爲中心的一致性模型
-
最終一致性(Eventual Consistency)
最終一致性是指若是更新的間隔時間比較長,那麼全部的副本都可以最終達到一致性。
用戶讀到某一操做對系統特定數據的更新須要一段時間,咱們將這段時間稱爲「不一致性窗口」。
在讀多寫少的場景中,例如CDN,讀寫之比很是懸殊,若是網站的運營人員修改了一張圖片,最終用戶延遲了一段時間纔看到這個更新實際上問題並非很大。
複製狀態機
複製狀態機的基本思想是:一個分佈式的複製狀態機系統由多個複製單元組成,每一個複製單元均是一個狀態機,它的狀態保存在一組狀態變量中。狀態機的狀態可以而且只能經過外部命令來改變。
上文提到的「一組狀態變量」一般是基於操做日誌來實現的。每個複製單元存儲一個包含一系列指令的日誌,而且嚴格按照順序逐條執行日誌上的指令。
因此,在複製狀態機模型下,一致性算法的主要工做就變成了如何保證操做日誌的一致性。
複製狀態機的運行過程以下圖所示:
服務器上的一致性模塊負責接收外部命令,而後追加到本身的操做日誌中。它與其餘服務器上的一致性模塊進行通訊以保證每個服務器上的操做日誌最終都以相同的順序包含相同的指令。一旦指令被正確複製,那麼每個服務器的狀態機都將按照操做日誌的順序來處理它們,而後將輸出結果返回給客戶端。
複製狀態機在分佈式系統中常被用於解決各類容錯相關的問題,例如,GFS、HDFS、Chubby、ZooKeeper和etcd等分佈式系統都是基於複製狀態機模型實現的。
須要注意的是,指令在狀態機上的執行順序並不必定等同於指令的發出順序或接收順序。
複製狀態機只是保證全部的狀態機都以相同的順序執行這些命令。
拜占庭將軍問題
拜占庭位於現在土耳其的伊斯坦布爾,是東羅馬帝國的首都。因爲當時拜占庭羅馬帝國幅員遼闊,出於防護的緣由,每一個軍隊都相隔甚遠,將軍與將軍之間只能靠信差來傳遞消息。發生戰爭時,拜占庭軍隊內全部將軍必需達成共識,決定是否攻擊敵人。可是軍隊內可能存在叛徒和敵軍的間諜擾亂將軍們的決定,所以在進行共識交流時,結果可能並不能真正表明大多數人的意見。這時,在已知有成員不可靠的狀況下,其他忠誠的將軍如何排除叛徒或間諜的影響來達成一致的決定,就是著名的拜占庭將軍問題。
拜占庭將軍問題講述的是一個共識問題。拜占庭將軍問題是對現實世界的模型化。
-
拜占庭錯誤是一個overly pessimistic模型(最悲觀、最強的錯誤模型)
研究這個模型的意義在於:若是某個一致性協議可以保證系統在出現N個拜占庭錯誤時,依舊能夠作出一致性決定,那麼這個協議也就可以處理系統出現N個其餘任意類型的錯誤。
-
進程失敗錯誤(fail-stop Failure,如同宕機)則是一個overly optimistic模型(最樂觀、最弱的錯誤模型)
研究這個模型的意義在於:若是某個一致性協議在系統出現N個進程失敗錯誤時都沒法保證作出一致性決定,那麼這個協議也就沒法處理系統出現N個其餘任意類型的錯誤。
Fred Schneider在前面提到的論文《Implementing fault-tolerant services using thestate machine approach》中指出了這樣一個基本假設:
一個RSM(分佈式狀態機)系統要容忍N個拜占庭錯誤,至少須要2N+1個複製節點。
若是隻是把錯誤的類型縮小到進程失敗,則至少須要N+1個複製節點才能容錯。
可是不是隻要知足上文提到的2N+1個要求就能保證萬無一失了呢?很不幸,答案是否認的。
FLP不可能性
FLP不可能性是分佈式領域中一個很是著名的定理:
No completely asynchronous consensusprotocol can tolerate even a single unan-nounced process death.
在異步通訊場景下,任何一致性協議都不能保證,即便只有一個進程失敗,其餘非失敗進程也不能達成一致。
這裏的進程失敗(unannounced process death)指的是一個進程發生了故障,但其餘節點並不知道,繼續認爲這個進程尚未處理完成或發生消息延遲了。
舉個例子:
甲、乙、丙三我的各自分開進行投票(投票結果是0或1)。他們彼此能夠經過電話進行溝通,但有人會睡着。例如:甲投票0,乙投票1,這時候甲和乙打平,丙的選票就很關鍵。然而丙睡着了,在他醒來以前甲和乙都將沒法達成最終的結果。即便從新投票,也有可能陷入無盡的循環之中。
FLP定理實際上說明了在容許節點失效的場景下,基於異步通訊方式的分佈式協議,沒法確保在有限的時間內達成一致性。用CAP理論解釋的話,在P的條件下,沒法知足C和A。
請注意,這個結論的前提是異步通訊。在分佈式系統中,「異步通訊」與「同步通訊」的最大區別是沒有時鐘、不能時間同步、不能使用超時、不能探測失敗、消息可任意延遲、消息可亂序等。
因此,實際的一致性協議(Paxos、Raft等)在理論上都是有缺陷的,最大的問題是理論上存在不可終止性!但他們都作了一些調整,下降了機率。
Paxos協議
大神Leslie Lamport對相似拜占庭將軍這樣的問題進行了深刻研究,並發表了幾篇論文。
總結起來就是回答以下的三個問題:
1)相似拜占庭將軍這樣的分佈式一致性問題是否有解?
2)若是有解的話須要知足什麼樣的條件?
3)基於特定的前提條件,提出一種解法。
Leslie Lamport在論文「拜占庭將軍問題」中已經給出了前兩個問題的回答,而第三個問題在他的論文「The Part-Time Parliament」中提出了一種基於消息傳遞的一致性算法。
下面講述的就是大神的平常操做:
1990年,Lamport向ACM Transac-tions on Computer Systems提交了他那篇關於Paxos算法的論文。主編回信建議他用數學而不是神話描述他的算法,不然他們不會考慮接受這篇論文。Lamport以爲那些人太迂腐,拒絕作任何修改,轉而將論文貼在了本身的我的博客上。
起初Paxos算法因爲難以理解並無引發多少人的重視,直到2006年Google的三大論文初現「雲」端,其中Chubby Lock服務使用了Paxos做爲Chubby Cell的一致性算法,這件事使得Paxos算法的人氣今後一路飆升,幾乎壟斷了一致性算法領域。在Raft算法誕生以前,Paxos幾乎成了一致性協議的代名詞。
Lamport本人以爲Paxos很簡單,但事實上對於大多數人來講,Paxos仍是太難理解了。
引用NSDI社區上的一句話就是:全世界真正理解Paxos算法的人只有5個!
這可能就是人和神之間的區別吧。
而後,更容易理解的一致性算法Raft誕生了。
Raft協議:爲可理解性而生
終於講到Raft了,我太不容易了。
Raft算法主要使用兩種方法來提升可理解性。提升理解性主要經過兩個經常使用手段
-
問題分解
儘量地將問題分解成爲若干個可解決的、更容易理解的小問題——這是衆所周知的簡化問題的方法論。例如,Raft算法把問題分解成了領袖選舉(leader election)、日誌複製(log repli-cation)、安全性(safety)和成員關係變化(membershipchanges)這幾個子問題。
- 領袖選舉:在一個領袖節點發生故障以後必須從新給出一個新的領袖節點。
- 日誌複製:領袖節點從客戶端接收操做請求,而後將操做日誌複製到集羣中的其餘服務器上,而且強制要求其餘服務器的日誌必須和本身的保持一致。
- 安全性:Raft關鍵的安全特性是下文提到的狀態機安全原則(State Machine Safety)——若是一個服務器已經將給定索引位置的日誌條目應用到狀態機中,則全部其餘服務器不會在該索引位置應用不一樣的條目。下文將會證實Raft是如何保證這條原則的。
- 成員關係變化:配置發生變化的時候,集羣可以繼續工做。
-
減小狀態空間
Raft算法經過減小須要考慮的狀態數量來簡化狀態空間。這將使得整個系統更加一致而且可以儘量地消除不肯定性。
Raft有幾點重要的創新
- 強領導人。Raft使用一種比其餘算法更強的領導形式。例如,日誌條目只從領導人發向其餘服務器。這樣就簡化了對日誌複製的管理,提升了Raft的可理解性。
- 領袖選舉。Raft使用隨機定時器來選舉領導者。這種方式僅僅是在全部算法都須要實現的心跳機制上增長了一點變化,就使得衝突解決更加簡單和快速。
- 成員變化。Raft在調整集羣成員關係時使用了新的一致性(joint consensus,聯合一致性)方法。使用這種方法,使得集羣配置在發生改變時,集羣依舊可以正常工做。
Raft一致性算法
基本概念
-
Leader(領袖)
-
Candidate(候選人)
-
Follower(羣衆)
-
任期(Term):Raft算法將時間劃分紅爲任意個不一樣長度的任期,任期是單調遞增的,用連續的數字(1, 2, 3……)表示。在Raft的世界裏,每個任期的開始都是一次領導人的選舉。若是一個候選人贏得了選舉,那麼它就會在該任期的剩餘時間內擔任領導人。在某些狀況下,選票會被瓜分,致使沒有哪位候選人可以獲得超過半數的選票,這樣本次任期將以沒有選出領導人而結束。那麼,系統就會自動進入下一個任期,開始一次新的選舉。Raft算法保證在給定的一個任期內最多隻有一個領導人。某些Term會因爲選舉失敗,存在沒有領導人的狀況,如t3所示。
任期在Raft中起着邏輯時鐘的做用,同時也可用於在Raft節點中檢測過時信息——好比過時的領導人。每一個Raft節點各自都在本地維護一個當前任期值,觸發這個數字變化(增長)主要有兩個場景:開始選舉和與其餘節點交換信息。若是一個節點(包括領導人)的當前任期號比其餘節點的任期號小,則將本身本地的任期號自覺地更新爲較大的任期號。若是一個候選人或者領導人意識到它的任期號過期了(比別人的小),那麼它會馬上切換回羣衆狀態;若是一個節點收到的請求所攜帶的任期號是過期的,那麼該節點就會拒絕響應本次請求。
領導人選舉
Raft經過選舉一個權力至高無上的領導人,並採起賦予他管理複製日誌重任的方式來維護節點間複製日誌的一致性。
領導人從客戶端接收日誌條目,再把日誌條目複製到其餘服務器上,而且在保證安全性的前提下,告訴其餘服務器將日誌條目應用到它們的狀態機中。領導人能夠決定新的日誌條目須要放在日誌文件的什麼位置,而不須要和其餘服務器商議,而且數據都是單向地從領導人流向其餘服務器。
領導人選舉的過程,就是Raft三種角色切換的過程
開始的時候,系統有一個Leader和衆多Follower
- 每個Raft節點(包含Follower)內有一個選舉定時器,若是收到Leader廣播的心跳包,則Follower將選舉定時器重置。
- 若是Follower的選舉定時器時間到了,在這個期間沒有收到任何心跳包,Follower認爲Leader,本身能夠當Leader了,就開始發起選舉,主要作以下步驟
- 將本身本地維護的當前任期號(current_term_id)加1
- 將本身的狀態切換到候選人(Candidate),併爲本身投票
- 向其所在集羣中的其餘節點發送RequestVote RPC(RPC消息會攜帶「current_term_id」值),要求它們投票給本身
- 設置選舉超時時間,通常是隨機選取一個區間中的值
- 在Candidate狀態下
- 若是獲得大多數選票,則成爲Leader
- 若是發現已經產生了新的領導人或者有更大的任期,則狀態更改成Follower
- 若是選舉超時時間過了,候選人自增任期號(Term++)而且發起新一輪的拉選票活動
- 在Leader狀態下,若是發現了更高的任期,則將本身變動爲Follower
日誌複製
一旦某個領導人贏得了選舉,那麼它就會開始接收客戶端的請求。領導人將把這條指令做爲一條新的日誌條目加入它的日誌文件中,而後並行地向其餘Raft節點發起AppendEntriesRPC,要求其餘節點複製這個日誌條目。當這個日誌條目被「安全」地複製以後,Leader會將這條日誌應用(apply,即執行該指令)到它的狀態機中,而且向客戶端返回執行結果。若是Follower發生錯誤,運行緩慢沒有及時響應AppendEntries RPC,或者發生了網絡丟包的問題,那麼領導人會無限地重試AppendEntries RPC(甚至在它響應了客戶端以後),直到全部的追隨者最終存儲了和Leader同樣的日誌條目。
日誌由有序編號的日誌條目組成。每個日誌條目通常均包含三個屬性:整數索引(log index)、任期號(term)和指令(command)。通常以下所示:
一旦領導人建立的條目已經被複制到半數以上的節點上了,那麼這個條目就稱爲可被提交的。
Raft日誌複製主要流程以下:
- 客戶端向Leader發送寫請求。
- Leader將寫請求解析成操做指令追加到本地日誌文件中。
- Leader爲每一個Follower廣播AppendEntries RPC。
- Follower經過一致性檢查,選擇從哪一個位置開始追加Leader的日誌條目。
- 一旦日誌項提交成功,Leader就將該日誌條目對應的指令應用(apply)到本地狀態機,並向客戶端返回操做結果。
- Leader後續經過AppendEntries RPC將已經成功(在大多數節點上)提交的日誌項告知Follower。
- Follower收到提交的日誌項以後,將其應用至本地狀態機。
從上面的步驟能夠看出,針對Raft日誌條目有兩個操做,提交(commit)和應用(apply),應用必須發生在提交以後,即某個日誌條目只有被提交以後才能被應用到本地狀態機上。
流程圖以下:https://www.processon.com/view/link/5fa6c1045653bb25634dea4a
安全性
本文介紹如何在領導人選舉部分加入一個限制規則來保證——任何的領導人都擁有以前任期提交的所有日誌條目。
-
怎樣才能具備成爲領導人的資格?- 必須包含全部已提交日誌條目
RequestVote RPC的接收方有一個檢查:若是Follower本身的日誌比RPC調用方(候選人)的日誌更加新,就會拒絕候選人的投票請求。
這就Raft算法使用投票的方式來阻止那些沒有包含全部已提交日誌條目的節點贏得選舉。
-
如何判斷日誌已經提交?
在提交以前term的日誌項時,必須保證當前term新建的日誌項已經複製到超過半數節點。這樣,以前term的日誌項纔算真正提交的。
可用性與時序性
broadcastTime << electionTimeout << MTBF
broadcastTime指的是一個節點向集羣中其餘節點發送RPC,而且收到它們響應的平均時間。
electionTimeout就是選舉超時時間。
MTBF指的是單個節點發生故障的平均時間間隔。
爲了使領導人可以持續發送心跳包來阻止下面的Follower發起選舉,broadcastTime應該比electionTimeout小一個數量級。
異常狀況
####追隨者/候選人異常
Raft算法經過領導人無限的重試來應對這些失敗,直到故障的節點重啓並處理了這些RPC爲止。
由於Raft算法中的RPC都是冪等的,所以不會有什麼問題。
領導人異常
Raft數據交換流程如上圖所示,在任什麼時候刻,領導人都有可能崩潰。
- 數據在到達Leader以前
不影響一致性
- 數據到達Leader節點,但未複製到Follower節點
不影響一致性
若是在這個階段Leader出現故障,此時數據屬於未提交狀態,那麼Client不會收到ACK,而是會認爲超時失敗可安全發起重試。Follower節點上沒有該數據,從新選主後Client重試從新提交可成功。原來的Leader節點恢復以後將做爲Follower加入集羣,從新從當前任期的新Leader處同步數據,與Leader數據強制保持一致。
- 數據到達Leader節點,成功複製到Follower的部分節點上,但還未向Leader響應接收
數據不丟失,也不影響一致性
若是在這個階段Leader出現故障,此時數據在Follower節點處於未提交狀態(Uncommitted)且不一致,那麼Raft協議要求投票只能投給擁有最新數據的節點。因此擁有最新數據的節點會被選爲Leader,再將數據強制同步到Follower,數據不會丟失而且可以保證最終一致。
- 數據到達Leader節點,成功複製到Follower的全部節點上,但還未向Leader響應接收
數據不丟失,也不影響一致性
若是在這個階段Leader出現故障,雖然此時數據在Fol-lower節點處於未提交狀態(Uncommitted),但也能保持一致,那麼從新選出Leader後便可完成數據提交,因爲此時客戶端不知到底有沒有提交成功,所以可重試提交。針對這種狀況,Raft要求RPC請求實現冪等性,也就是要實現內部去重機制。
- 數據到達Leader節點,成功複製到Follower的全部或大多數節點上,數據在Leader上處於已提交狀態,但在Follower上處於未提交狀態
數據不丟失,也不影響一致性
- 數據到達Leader節點,成功複製到Follower的全部或大多數節點上,數據在全部節點都處於已提交狀態,但還未響應Client
數據不丟失,也不影響一致性
- 網絡分區致使的腦裂狀況,出現雙Leader
不影響一致性
網絡分區將原先的Leader節點和Follower節點分隔開,Follower收不到Leader的心跳將發起選舉產生新的Leader。這時就產生了雙Leader,原先的Leader獨自在一個區,向它提交數據不可能複製到大多數節點上,因此永遠都是提交不成功。向新的Leader提交數據能夠提交成功,網絡恢復後舊的Leader發現集羣中有更新任期(Term)的新Leader,則自動降級爲Fol-lower並重新Leader處同步數據達成集羣數據一致。
總結
分佈式系統和通常的業務系統區別仍是挺大的,涉及到更多論文、數理知識,更加偏學術一些。隨着計算機的不斷髮展,關於分佈式的知識,仍是須要進行掌握的。
關於Raft算法,建議看一下源碼https://github.com/etcd-io/etcd/tree/master/raft。
Raft經過領導選舉機制,簡化了總體的複雜性。利用日誌複製+複製狀態機,保證狀態執行的一致。同時設置了一些對應的安全規則,增強了日誌複製的安全,維護了一致性。
若是你們時間有限,能夠只看一下CAP、複製狀態機和Raft。
資料
-
https://www.infoq.cn/article/wechat-serial-number-generator-architecture/ 微信序列號生成器架構設計及演變
-
https://www.jianshu.com/p/ab511132a34f raft 系列解讀(3) 之 代碼實現
-
https://blog.csdn.net/lanyang123456/article/details/109279234 raft協議中的日誌安全性
-
http://www.duokan.com/book/180790 雲原生分佈式存儲基石:etcd深刻解析
最後
你們若是喜歡個人文章,能夠關注個人公衆號(程序員麻辣燙)
個人我的博客爲:https://shidawuhen.github.io/
往期文章回顧:
技術
- 微服務之服務框架和註冊中心
- Beego框架使用
- 淺談微服務
- TCP性能優化
- 限流實現1
- Redis實現分佈式鎖
- Golang源碼BUG追查
- 事務原子性、一致性、持久性的實現原理
- CDN請求過程詳解
- 記博客服務被壓垮的歷程
- 經常使用緩存技巧
- 如何高效對接第三方支付
- Gin框架簡潔版
- InnoDB鎖與事務簡析
- 算法總結
- 分佈式系統與一致性協議
讀書筆記
思考