1、共識算法 -- 拜占庭問題算法
兩忠一叛問題:數據庫
如上圖所示,將軍A、B、C約定同時進攻或者撤退,假如將軍C叛變了,被中間人截取消息併發送進攻給A、撤退給B,當全部將軍消息都收到後結果以下:
A:2票進攻1票撤退;
B:2票撤退1票進攻;
致使最終A獨自去攻打敵軍,B撤退,最終會任務失敗。後端
口信消息型拜占庭問題之解服務器
如上圖所示,通過2輪協商能夠解決上述的兩忠一叛問題;第一輪協商由leader發起,向其他3個將軍發送進攻的指令消息,若是未收到消息則默認撤退指令;第二輪協商爲3個將軍之間的互通消息,假如將軍C叛變爲干擾信息向將軍A、B發送撤退消息,則最終結果:
A:2進攻1撤退;
B:2進攻1撤退;
最終會執行進攻指令,這樣解決了兩忠一叛問題。網絡
若是叛將人數爲m,將軍總人數不能少於3m + 1 。叛將數m決定遞歸循環的次數(將軍們要進行多少輪做戰信息協商),即m+1輪,n位將軍,最多能容忍(n - 1) / 3位叛將。併發
2、分佈式一致性算法前奏之Quorum NWR算法分佈式
Quorum選舉算法
在N個副本中,一次更新成功的若是有W個,那麼在讀取數據時是要從大於N-W個副本中讀取,這樣就能至少讀到一個更新的數據了。
如:維護了10個副本,一次成功更新了三個,那麼至少須要讀取八個副本的數據,能夠保證讀到最新的數據。性能
WARO算法(Write All Read one)
只有當全部的副本都更新成功以後,此次寫操做纔算成功,不然視爲失敗。WARO 優先保證讀服務,由於全部的副本更新成功,才能視爲更新成功,從而保證了全部的副本一致,這樣的話,只須要讀任何一個副本上的數據便可。寫服務的可用性較低,由於只要有一個副本更新失敗,這次寫操做就視爲失敗了。假設有 N 個副本,N-1 個都宕機了,剩下的那個副本仍能提供讀服務;可是隻要有一個副本宕機了,寫服務就不會成功。WARO 犧牲了更新服務的可用性,最大程度地加強了讀服務的可用性,而 Quorum 就是在更新服務和讀服務之間進行的一個折衷。學習
Quorum的應用
Quorum機制沒法保證強一致性,也就是沒法實現任什麼時候刻任何用戶或節點均可以讀到最近一次成功提交的副本數據。
Quorum機制的使用須要配合一個獲取最新成功提交的版本號的metadata服務,這樣能夠肯定最新已經成功提交的版本號,而後從已經讀到的數據中就能夠確認最新寫入的數據。
Quorum是分佈式系統中經常使用的一種機制,用來保證數據冗餘和最終一致性的投票算法,在Paxos、Raft和ZooKeeper的Zab等算法中,均可以看到Quorum機制的應用。優化
如上圖所示,DATA-1有2個副本,DATA-2有3個副本,DATA-3 有1個副本,副本的數量即表示N;對DATA-2執行寫操做時,完成了2個副本的更新(節點B、C),才完成寫操做,即W此時爲2;對DATA-2執行讀操做,客戶端讀取DATA-2的數據時,須要讀取2個副本中的數據,而後返回最新的那份數據,即讀副本R爲2。
不管客戶端如何執行讀操做,即便訪問寫操做未強制更新副本節點B,執行讀操做時,由於要讀2份數據副本,因此除了節點B上的DATA-2,還會讀取節點C上的DATA-2,而節點C的DATA-2數據副本是強制更新成功的,這個時候返回給客戶端確定是最新的那份數據。
對於Quorum:
當W+R>N的時候,對於客戶端來說,整個系統能保證強一致性,必定能返回更新後的那份數據。
當W+R<=N的時候,對於客戶端來說,整個系統只能保證最終一致性,可能會返回舊數據。
3、paxos算法
paxos算法由Proposer、Acceptor和Learner組成:
提議者(Proposer):提議一個值,用於投票表決。在絕大多數場景中,集羣中收到客戶端請求的節點,纔是提議者。這樣作的好處是,對業務代碼沒有入侵性,也就是說,咱們不須要在業務代碼中實現算法邏輯,就能夠像使用數據庫同樣訪問後端的數據。
接受者(Acceptor):對每一個提議的值進行投票,並存儲接受的值,好比A、B、C三個節點。 通常來講,集羣中的全部節點都在扮演接受者的角色,參與共識協商,並接受和存儲數據。
學習者(Learner):被告知投票的結果,接受達成共識的值,存儲保存,不參與投票的過程。通常來講,學習者是數據備份節點,好比「Master-Slave」模型中的Slave,被動地接受數據,容災備份。
在Basic Paxos中,使用提案表明一個提議,在提案中除了提案編號,還包含了提議值。使用[n, v]表示一個提案,其中n爲提案編號,v爲提議值。
整個共識協商是分2個階段進行的(二階段提交)。假設客戶端1的提案編號爲1,客戶端2的提案編號爲5,節點A、B先收到來自客戶端1的準備請求,節點C先收到來自客戶端2的準備請求。
prepare階段
客戶端一、2做爲提議者,分別向全部接受者發送包含提案編號的準備請求:
對於客戶端1的提案,因爲以前沒有經過任何提案,因此節點A、B之後將再也不響應提案編號小於等於1的prepare請求,即不會經過編號小於1的提案,節點C之後再也不響應提案編號小於等於5的準備請求,即不會經過編號小於5的提案;
對於客戶端2的提案,當節點A、B收到提案編號爲5的準備請求的時候,由於提案編號5大於它們以前響應的準備請求的提案編號1,並且兩個節點都沒有經過任何提案,因此它將返回一個 「尚無提案」的響應,因此節點A、B將再也不響應提案編號小於等於5的準備請求,即不會經過編號小於5的提案,
當節點C收到提案編號爲1的準備請求的時候,因爲提案編號1小於它以前響應的準備請求的提案編號5,因此丟棄該準備請求不作響應;
Accept階段
首先客戶端一、2在收到大多數節點的準備響應以後,會分別發送接受請求:
當客戶端1收到大多數的接受者(節點A、B)的準備響應後,根據響應中提案編號最大的提案的值設置接受請求中的值。由於節點A、B的準備響應中都爲空,因此就把本身的提議值3做爲提案的值,發送接受請求[1, 3];
當客戶端2收到大多數的接受者的準備響應後(節點A、B和節點C),根據響應中提案編號最大的提案的值,來設置接受請求中的值。由於該值在來自節點A、B、C的準備響應中都爲空,因此就把本身的提議值7做爲提案的值,發送接受請求[5, 7];
當三個節點收到2個客戶端的接受請求時:
當節點A、B、C收到接受請求[1, 3]的時候,因爲提案的提案編號1小於三個節點承諾能經過的提案的最小提案編號5,因此提案[1, 3]將被拒絕;
當節點A、B、C收到接受請求[5, 7]的時候,因爲提案的提案編號5不小於三個節點承諾能經過的提案的最小提案編號5,因此就經過提案[5, 7],也就是接受了值7,三個節點就X值爲7達成了共識;
Multi-Paxos
Basic Paxos只能就單個值(Value)達成共識,Multi-Paxos是經過多個Basic Paxos實例實現一系列值的共識的算法。
Multi-Paxos經過引入Leader節點,將Leader節點做爲惟一提議者,避免了多個提議者同時提交提案的狀況,解決了提案衝突的問題,
Leader節點是經過執行Basic Paxos算法,進行投票選舉產生的。
優化Basic Paxos執行能夠採用「當領導者處於穩定狀態時,省掉準備階段,直接進入接受階段」這個優化機制。在Leader節點上,序列中的命令是最新的,再也不須要經過準備請求來發現以前被大多數節點經過的提案,Leader能夠獨立指定提案中的值。Leader節點在提交命令時,能夠省掉準備階段,直接進入到接受階段:
和重複執行Basic Paxos相比,Multi-Paxos引入領導者節點以後,由於只有領導者節點一個提議者,因此就不存在提案衝突。另外,當主節點處於穩定狀態時,就省掉準備階段,直接進入接受階段,因此在很大程度上減小了往返的消息數,提高了性能,下降了延遲。
4、一致hash算法
使用哈希算法的問題?
經過哈希算法,每一個key均可以尋址到對應的服務器,好比,查詢key是key-01,計算公式爲hash(key-01) %3 ,通過計算尋址到了編號爲1的服務器節點A;
若是服務器數量發生變化,基於新的服務器數量來執行哈希算法的時候,就會出現路由尋址失敗的狀況,沒法找到以前尋址到的那個服務器節點;假如增長了一個節點,節點的數量從3變化爲4,那麼以前的hash(key-01) %3 = 1,就變成了hash(key-01) %4 =X,由於取模運算髮生了變化,因此這個X大機率不是1,這時再查詢就會找不到數據了,由於key-01對應的數據並不是存儲在節點X。
經過上圖能夠看出,當擴容增長一個節點時會出現hash尋址失敗的狀況;同理,若是須要下線1個服務器節點,也會存在相似的可能查詢不到數據的問題。
一致哈希實現哈希尋址
一致哈希算法也用了取模運算,但與哈希算法不一樣的是,哈希算法是對節點的數量進行取模運算,而一致哈希算法是對2^32進行取模運算。在一致哈希中,能夠經過執行哈希算法將節點映射到哈希環上,如選擇節點的主機名做爲參數執行hash(),那麼每一個節點就能肯定其在哈希環上的位置了。
當須要對指定key的值進行讀寫的時候,能夠經過下面2步進行尋址:
首先,將key做爲參數執行hash()計算哈希值,並肯定此key在環上的位置;
而後,從這個位置沿着哈希環順時針「行走」,遇到的第一節點就是key對應的節點。
根據一致哈希算法,key-01將尋址到節點A,key-02將尋址到節點B,key-03將尋址到節點C。假設如今節點C故障了,key-01和key-02不會受到影響,只有key-03的尋址被重定位到A。在一致哈希算法中,若是某個節點宕機不可用了,那麼受影響的數據僅僅是會尋址到此節點和前一節點之間的數據。好比當節點C宕機了,受影響的數據是會尋址到節點B和節點C之間的數據(例如key-03),尋址到其餘哈希環空間的數據不會受到影響。同理,若是集羣擴容一個節點,在一致哈希算法中,若是增長一個節點,受影響的數據僅僅是會尋址到新節點和前一節點之間的數據,其它數據也不會受到影響。
在哈希尋址中常出現這樣的問題:客戶端訪問請求集中在少數的節點上,出現了有些機器高負載,有些機器低負載的狀況,在一致哈希中可使用虛擬節點讓數據訪問分佈的比較均勻。
使用虛擬節點解決冷熱不均的問題:
對每個服務器節點計算多個哈希值,在每一個計算結果位置上,都放置一個虛擬節點,並將虛擬節點映射到實際節點。
好比,能夠在主機名的後面增長編號,分別計算 「Node-A-01」「Node-A-02」「Node-B-01」「Node-B-02」「Node-C-01」「Node-C-02」的哈希值,因而造成6個虛擬節點;增長了節點後,節點在哈希環上的分佈就相對均勻了。若是有訪問請求尋址到「Node-A-01」這個虛擬節點,將被重定位到節點A。
所以,當節點數越多的時候,使用哈希算法時,須要遷移的數據就越多,使用一致哈希時,須要遷移的數據就越少。因此相比hash算法,
一致哈希算法具備較好的容錯性和可擴展性。
5、zab協議
Multi-Paxos解決的是一系列值如何達成共識的問題,不關心最終達成共識的值是什麼,不關心各值的順序,即它不關心操做的順序性。
ZAB協議基於主備模式的原子廣播,最終實現了操做的順序性。Master-Slave的主備模型,主節點採用二階段提交,向備份節點同步數據,若是主節點發生故障,數據最完備的節點將當選主節點;原子廣播協議,廣播一組消息,消息的順序是固定的。
ZAB支持3種成員身份(領導者、跟隨者、觀察者)。
領導者(Leader): 做爲主節點,在同一時間集羣只會有一個領導者,全部的寫請求都必須在領導者節點上執行。
跟隨者(Follower):做爲備份節點, 集羣能夠有多個跟隨者,它們會響應領導者的心跳,並參與領導者選舉和提案提交的投票,跟隨者能夠直接處理並響應來自客戶端的讀請求,但對於寫請求,跟隨者須要將它轉發給領導者處理。
觀察者(Observer):做爲備份節點,相似跟隨者,可是沒有投票權,觀察者不參與領導者選舉和提案提交的投票。
ZAB在Multi-Paxos的基礎上作了優化,爲了實現分區容錯能力,將數據複製到大多數節點後,領導者就會進入提交執行階段,通知備份節點執行提交操做。
ZAB定義了4種成員狀態:
LOOKING:選舉狀態,該狀態下的節點認爲當前集羣中沒有領導者,會發起領導者選舉。
FOLLOWING :跟隨者狀態,意味着當前節點是跟隨者。
LEADING :領導者狀態,意味着當前節點是領導者。
OBSERVING: 觀察者狀態,意味着當前節點是觀察者。
如上圖所示,
首先,當跟隨者檢測到鏈接領導者節點的讀操做等待超時了,跟隨者會變動節點狀態,將本身的節點狀態變動成LOOKING,而後發起領導者選舉;
接着,每一個節點會建立一張選票,這張選票是投給本身的,而後各自將選票發送給集羣中全部節點,
通常而言,節點會先接收到本身發送給本身的選票(由於不須要跨節點通信,傳輸更快);
集羣的各節點收到選票後,爲了選舉出數據最完整的節點,對於每一張接收到選票,節點都須要進行領導者PK,也就將選票提議的領導者和本身提議的領導者進行比較,找出更適合做爲領導者的節點,約定的規則以下:
優先檢查任期編號(Epoch),任期編號大的節點做爲領導者;
若是任期編號相同,比較事務標識符的最大值,值大的節點做爲領導者;
若是事務標識符的最大值相同,比較集羣ID,集羣ID大的節點做爲領導者。
若是選票提議的領導者,比本身提議的領導者,更適合做爲領導者,那麼節點將調整選票內容,推薦選票提議的領導者做爲領導者。
zab故障恢復是由成員發現和數據同步兩個階段完成的,成員發現是經過跟隨者和領導者交互來完成的,目標是確保大多數節點對領導者的領導關係沒有異議,也就是確立領導者的領導地位:
成員發現,是爲了創建跟隨者和領導者之間的領導者關係,並經過任期編號來確認這個領導者是否爲最合適的領導者。
當跟隨者和領導者設置ZAB狀態爲數據同步,它們也就是進入了數據同步階段,數據同步也是經過跟隨者和領導者交互來完成的,目標是確保跟隨者節點上的數據與領導者節點上數據是一致的。
數據同步,是經過以領導者的數據爲準的方式,來實現各節點數據副本的一致,須要你注意的是,基於「大多數」的提交原則和選舉原則,能確保被複制到大多數節點並提交的提案,就再也不改變。
對於zab處理寫請求:
因爲寫請求只能在領導者節點上處理,因此ZooKeeper集羣寫性能約等於單機。而讀請求是能夠在全部的節點上處理的,因此讀性能是能水平擴展的。能夠經過分集羣的方式來突破寫性能的限制,
並經過增長更多節點,來擴展集羣的讀性能。
首先,ZAB實現了主備模式,也就是全部的數據都以主節點爲準;
其次,ZAB實現了FIFO隊列,保證消息處理的順序性。
另外,ZAB還實現了當主節點崩潰後,只有日誌最完備的節點才能當選主節點,由於日誌最完備的節點包含了全部已經提交的日誌,因此這樣就能保證提交的日誌不會再改變。
6、raft算法
Raft算法是分佈式系統開發首選的共識算法 ,從本質上說,Raft算法是經過一切以領導者爲準的方式,實現一系列值的共識和各節點日誌的一致。
Raft算法支持領導者(Leader)、跟隨者(Follower)和候選人(Candidate)3種狀態:
跟隨者:就至關於普通羣衆,默默地接收和處理來自領導者的消息,當等待領導者心跳信息超時的時候,就主動站出來,推薦本身當候選人。
候選人:候選人將向其餘節點發送請求投票(RequestVote)RPC消息,通知其餘節點來投票,若是贏得了大多數選票,就晉升當領導者。
領導者:主要工做內容就是3部分,處理寫請求、管理日誌複製和不斷地發送心跳信息。
選舉領導者的過程:
首先,在初始狀態下,集羣中全部的節點都是跟隨者的狀態,每一個節點等待領導者節點心跳信息的超時時間間隔是隨機的。集羣中沒有領導者,而節點A的等待超時時間最小(150ms),它會最早由於沒有等到領導者的心跳信息而發生超時。節點A就增長本身的任期編號,並推舉本身爲候選人,先給本身投上一張選票,而後向其餘節點發送請求投票RPC消息,請求它們選舉本身爲領導者。當候選人節點A在選舉超時時間內贏得了大多數的選票,那麼它就會成爲本屆任期內新的領導者。
節點A當選領導者後,將週期性地發送心跳消息,通知其餘服務器以阻止跟隨者發起新的選舉。
Raft算法中約定的選舉規則:
1. 領導者週期性地向全部跟隨者發送心跳消息,阻止跟隨者發起新的選舉。
2. 若是在指定時間內,跟隨者沒有接收到來自領導者的消息,那麼它就認爲當前沒有領導者,推舉本身爲候選人,發起領導者選舉。
3. 在一次選舉中,贏得大多數選票的候選人,將晉升爲領導者。
4. 在一個任期內,領導者一直都會是領導者,直到它自身出現問題(好比宕機),或者由於網絡延遲,其它節點發起一輪新的選舉。
5. 在一次選舉中,每個服務器節點會按照「先來先服務」的原則進行投票。
6. 當任期編號相同時,日誌完整性高的跟隨者(最後一條日誌項對應的任期編號值更大,索引號更大),拒絕投票給日誌完整性低的候選人。好比節點B、C的任期編號都是3,節點B的最後一條日誌項對應的任期編號爲3,而節點C爲2,那麼當節點C請求節點B投票給本身時,節點B將拒絕投票。
Raft算法日誌複製流程:
Raft算法中,副本數據是以日誌的形式存在的,領導者接收到來自客戶端寫請求後,處理寫請求的過程就是一個複製和應用日誌項到狀態機的過程。
首先,領導者進入第一階段,經過日誌複製(AppendEntries)RPC消息,將日誌項複製到集羣其餘節點上。
接着,若是領導者接收到大多數的「複製成功」響應後,它將日誌項應用到它的狀態機,並返回成功給客戶端。若是領導者沒有接收到大多數的「複製成功」響應,那麼就返回錯誤給客戶端。
1. 接收到客戶端請求後,領導者基於客戶端請求中的指令,建立一個新日誌項,並附加到本地日誌中。
2. 領導者經過日誌複製RPC,將新的日誌項複製到其餘的服務器。
3. 當領導者將日誌項成功複製到大多數的服務器上的時候,領導者會將這條日誌項應用到它的狀態機中。
4. 領導者將執行的結果返回給客戶端。
5. 當跟隨者接收到心跳信息,或者新的日誌複製RPC消息後,若是跟隨者發現領導者已經提交了某條日誌項,而它還沒應用,那麼跟隨者就將這條日誌項應用到本地的狀態機中。
7、總結
ZAB協議在Multi-Paxos達成共識的基礎上實現了操做的順序性。
Raft算法和Multi-Paxos不一樣之處:
1. 在Raft中,不是全部節點都能當選領導者,只有日誌最完整的節點,才能當選領導者;
2. 日誌必須是連續的;
Raft算法與ZAB協議的異同點:
1. Raft採用的是「先到先得」的自定義投票算法。Raft的領導者選舉,須要通信的消息數更少,選舉也更快。
2. 對於日誌複製,Raft和ZAB相同,都是以領導者的日誌爲準來實現日誌一致,並且日誌必須是連續的,也必須按照順序提交。
3. 對於讀操做和一致性,ZAB的設計目標是操做的順序性,在ZooKeeper中默認實現的是最終一致性,讀操做能夠在任何節點上執行;而Raft的設計目標是強一致性(也就是線性一致性),因此Raft更靈活,Raft系統既能夠提供強一致性,也能夠提供最終一致性。
4. 對於寫操做,Raft和ZAB相同,寫操做都必須在領導者節點上處理。
5. 成員變動,ZAB不支持成員變動,當須要節點變動(好比擴容)時,必須重啓整個ZooKeeper集羣。Raft支持成員變動,不須要重啓機器,集羣是一直運行的,服務也不會中斷。
相比ZAB,Raft的設計更爲簡潔,Raft沒有引入相似ZAB的成員發現和數據同步階段,而是當節點發起選舉時,遞增任期編號,在選舉結束後,廣播心跳,直接創建領導者關係,而後向各節點同步日誌,來實現數據副本的一致性。