分布式系統(tǒng)中的那些一致性(CAP、BASE、2PC、3PC、Paxos、ZAB)
前言
工作過(guò)幾年的同學(xué),尤其是這幾年,大家或多或少都參與過(guò)分布式系統(tǒng)的開(kāi)發(fā),遇到過(guò)各式各樣“分布式”問(wèn)題,而遇到這些問(wèn)題去解決時(shí)就是我們對(duì)這個(gè)知識(shí)學(xué)習(xí)的過(guò)程。
不知道大家是否跟我一樣,每每搜索到“分布式”關(guān)鍵詞,總會(huì)出現(xiàn)各種“分布式理論”,比如CAP、BASE理論、2PC、3PC 以及 Paxos、Raft、ZAB 算法,而這些貌似跟一致性都有一定的關(guān)系。
在讀過(guò)數(shù)次與之相關(guān)的不同文章后,每次都會(huì)有不一樣的理解以及困惑,比如,CAP中的 C 怎么就強(qiáng)一致了?BASE 理論的定義怎么這么抽象?還有 Paxos、ZAB、Raft 都是一致性算法,奧… 干?。?!管求它。轉(zhuǎn)眼就又忘了,不曉得大家是否跟我一樣。
本文以我對(duì)這些一致性理論的理解進(jìn)行闡述,希望可以對(duì)大家有一點(diǎn)幫助。
CAP理論
CAP理論是Eric Brewer教授在2000年提出的,大概是這樣的:
在分布式系統(tǒng)中,數(shù)據(jù)一致性,分區(qū)容忍性,系統(tǒng)可用性是不可能同時(shí)達(dá)到的,只能保證其中兩項(xiàng)可以達(dá)到。而由于在互聯(lián)網(wǎng)交互應(yīng)用中,網(wǎng)絡(luò)不穩(wěn)定的情況總是存在,網(wǎng)絡(luò)分區(qū)始終是不可避免的,從而在設(shè)計(jì)分布式系統(tǒng)時(shí),總是考慮如何在數(shù)據(jù)一致性和系統(tǒng)可用性上進(jìn)行取舍。
理解誤導(dǎo)
通常在一些CAP的文章中可以看到很多類(lèi)似以下的說(shuō)法:
C(consistency)一致性:訪(fǎng)問(wèn)所有節(jié)點(diǎn)得到的結(jié)果是一致的。
A(Availability)可用性:請(qǐng)求在一定時(shí)間內(nèi)可以得到正確的響應(yīng)。
P(Partition tolerance)分區(qū)容錯(cuò)性:在網(wǎng)絡(luò)分區(qū)的情況下,系統(tǒng)仍能提供服務(wù)。
并且后面會(huì)跟上這句話(huà)分布式系統(tǒng)不能保證同時(shí)使用C、A和P,只能選擇CP或AP 。
這樣的說(shuō)法并沒(méi)有錯(cuò),因?yàn)樘岢鲈摾碚摰淖髡叽_實(shí)有提出:
Any distributed system cannot guarantly C,A,and P simultaneously
但是會(huì)誤導(dǎo)讀者去理解。比如在我之前看到上述說(shuō)法時(shí)會(huì)有幾個(gè)疑問(wèn):
- 對(duì)于分區(qū)容錯(cuò)性,我搞個(gè)集群就可以;對(duì)于一致性,我理解那就跟ACID中的C是不一樣的,更像是某個(gè)組件集群部署后節(jié)點(diǎn)之間的數(shù)據(jù)一致性,那應(yīng)該還是集群,為什么說(shuō)是分布式系統(tǒng)呢?
- 怎么不能保證同時(shí)使用C,A, P?怎么一致就不可用了?可用就不一致了?不沖突吧?
CAP是CAP,這個(gè)“CP”或“AP”先適當(dāng)存疑
正確的理解
后面去了解CAP理論的背景,得知作者寫(xiě)了2版來(lái)闡述CAP,最后一個(gè)版本中寫(xiě)道:
In a distributed system(a collection of interconnected nodes that share data),you can only have two out of the following three guarantees across a write/read pair: Consistency,Availability,and Partition Tolerance
在分布式系統(tǒng)(共享數(shù)據(jù)的互連節(jié)點(diǎn)的集合)中,在寫(xiě)/讀對(duì)中只能有以下三種保證中的兩種:一致性、可用性和分區(qū)容錯(cuò)
在這一個(gè)版本中的(共享數(shù)據(jù)的互連節(jié)點(diǎn)的集合)證實(shí)了我第一個(gè)疑問(wèn),至于為什么說(shuō)分布式系統(tǒng),我覺(jué)得應(yīng)該是集群屬于分布式系統(tǒng)中的一個(gè)場(chǎng)景。
至于第二個(gè)疑問(wèn)其實(shí)還是場(chǎng)景問(wèn)題,如果在沒(méi)有網(wǎng)絡(luò)分區(qū)的情況下,C,A是可以同時(shí)滿(mǎn)足的,如果出現(xiàn)了網(wǎng)絡(luò)分區(qū),C,A確實(shí)不可以同時(shí)滿(mǎn)足,舉個(gè)例子:如果來(lái)了一個(gè)寫(xiě)操作,如果要滿(mǎn)足一致性,意味著這幾個(gè)節(jié)點(diǎn)的數(shù)據(jù)要一致后,數(shù)據(jù)才能被訪(fǎng)問(wèn),但是出現(xiàn)了網(wǎng)絡(luò)分區(qū),就會(huì)等待網(wǎng)絡(luò)恢復(fù)或重試或者其他操作,必然滿(mǎn)足不了可用性的要求(在一定時(shí)間內(nèi)可以得到正確的響應(yīng))。反過(guò)來(lái),如果要在一定時(shí)間內(nèi)得到正確響應(yīng),在網(wǎng)絡(luò)分區(qū)的情況下,一致性必然也滿(mǎn)足不了了。
所以CAP理論是有前提和場(chǎng)景的,總結(jié)一下應(yīng)該是這樣的:分布式系統(tǒng)中存在共享數(shù)據(jù)的互連節(jié)點(diǎn),在網(wǎng)絡(luò)分區(qū)的前提下,不能保證同時(shí)保證可用性和一致性。
CAP理論的應(yīng)用
現(xiàn)在再說(shuō) Zookeeper 是 CP 架構(gòu)、Eureka 是 AP 架構(gòu) 應(yīng)該就不難理解了。
這兩個(gè)組件都符合 CAP 中的(共享數(shù)據(jù)的互連節(jié)點(diǎn)的集合),Zookeeper 集群是 Leader 在寫(xiě)數(shù)據(jù)時(shí)需要過(guò)半節(jié)點(diǎn)同意才會(huì)寫(xiě)入,客戶(hù)端才會(huì)讀取到這個(gè)值,所以說(shuō) Zookeeper 是 CP 架構(gòu);Eureka 集群是不論哪個(gè)節(jié)點(diǎn)在寫(xiě)數(shù)據(jù)時(shí)都會(huì)立即刷新本地?cái)?shù)據(jù)然后再同步給其他節(jié)點(diǎn),客戶(hù)端這個(gè)時(shí)候讀取不同節(jié)點(diǎn)時(shí)就會(huì)發(fā)現(xiàn)數(shù)據(jù)不一致,所以 Eureka 是 AP 架構(gòu)。
BASE理論
BASE是Basically Available(基本可用)、Soft state(軟狀態(tài))和 Eventually consistent(最終一致性)三個(gè)短語(yǔ)的縮寫(xiě)。
- 基本可用
基本可用是指分布式系統(tǒng)在出現(xiàn)不可預(yù)知故障的時(shí)候,允許損失部分可用性,不像 CAP 中的定義那樣嚴(yán)格(在一定時(shí)間內(nèi)可以得到正確的響應(yīng))。比如:
響應(yīng)時(shí)間上的損失。正常情況下,0.5秒之內(nèi)返回給用戶(hù)結(jié)果,但由于出現(xiàn)故障,會(huì)有重試等操作,3秒內(nèi)返回也是接受的。
系統(tǒng)功能上的損失:正常情況下,用戶(hù)可以順利完成每一筆訂單,但是在一些節(jié)日大促購(gòu)物高峰的時(shí)候,為了保護(hù)系統(tǒng)的穩(wěn)定性,部分用戶(hù)可能會(huì)被引導(dǎo)到一個(gè)降級(jí)頁(yè)面。
- 軟狀態(tài)
軟狀態(tài)指允許系統(tǒng)中的數(shù)據(jù)存在中間狀態(tài),這種中間狀態(tài)的存在不會(huì)影響系統(tǒng)的整體可用性。比如:在交易的場(chǎng)景中,因?yàn)闀?huì)存在交易失敗的情況,所以不會(huì)直接扣減 A 賬戶(hù)100到 B 賬戶(hù)上,而是先凍結(jié) A 賬戶(hù)100。
- 最終一致性
最終一致性是指在經(jīng)過(guò)一段時(shí)間后,軟狀態(tài)的數(shù)據(jù)達(dá)到一致的狀態(tài)。比如:在凍結(jié) A 賬戶(hù)100后,如果失敗那就扣減A賬戶(hù)凍結(jié)的100至可用余額中;交易成功再將 A 賬戶(hù)凍結(jié)的100扣減,并添加至 B 賬戶(hù)的可用余額。最終達(dá)到一致性。
有很多的文章說(shuō)是BASE理論是CAP理論的演進(jìn),這種說(shuō)法先存疑,先存疑。因?yàn)镃AP理論的場(chǎng)景是分布式系統(tǒng)(共享數(shù)據(jù)的互連節(jié)點(diǎn)的集合),而B(niǎo)ASE理論是滿(mǎn)足更多的場(chǎng)景的。
Paxos算法
Paxos算法是萊斯利·蘭伯特于1990年提出的一種基于消息傳遞且具有高度容錯(cuò)特性的一致性算法。
基于消息傳遞通信模型的分布式系統(tǒng),不可避免的會(huì)發(fā)生以下錯(cuò)誤:進(jìn)程可能會(huì)慢、被殺死或者重啟,消息可能會(huì)延遲、丟失、重復(fù)。Paxos 算法解決的問(wèn)題是在一個(gè)可能發(fā)生上述異常的分布式系統(tǒng)中如何就某個(gè)值達(dá)成一致,保證不論發(fā)生以上任何異常,都不會(huì)破壞決議的一致性。
如何保證一致性?
OK,通過(guò)下圖看看 Paxos 是如何保證一致性的。為了方便理解,圖中的議員暫且當(dāng)作一個(gè)注冊(cè)中心集群中的實(shí)例。
- 當(dāng)某個(gè)議員有某些想法時(shí)想讓其他議員認(rèn)可并批準(zhǔn),那么會(huì)以提案的方式進(jìn)行決定。
- 在提交一個(gè)提案時(shí)都會(huì)獲取到一個(gè)具有全局唯一性的、遞增的提案編號(hào)(N),然后發(fā)送給其他議員。
- 其他議員在收到這個(gè)編號(hào)后會(huì)與自己批準(zhǔn)過(guò)的提案中最大編號(hào)進(jìn)行比較:
如果沒(méi)有收到的編號(hào)(N)大,那么它就會(huì)將它已經(jīng)批準(zhǔn)過(guò)編號(hào)最大的提案響應(yīng)給提案者,意味著認(rèn)可這個(gè)提案。
如果比收到的編號(hào)(N)大,則代表編號(hào)(N)被處理過(guò),不做任何響應(yīng),意味著不認(rèn)可這個(gè)提案。
4.提案者在收到半數(shù)以上響應(yīng)后,就會(huì)再次發(fā)送一個(gè)確認(rèn)的請(qǐng)求給其它議員進(jìn)行執(zhí)行。
5.其他議員在收到確認(rèn)的請(qǐng)求后,發(fā)現(xiàn)沒(méi)有對(duì)編號(hào)大于N的提案請(qǐng)求做出過(guò)響應(yīng),它就批準(zhǔn)該提案。
這個(gè)我感覺(jué)是和2PC一樣,通過(guò)兩階段提交,最終確認(rèn)是否批準(zhǔn),只不過(guò)是實(shí)現(xiàn)細(xì)節(jié)以及使用場(chǎng)景不同罷了。當(dāng)然也會(huì)存在2PC中第二階段節(jié)點(diǎn)失去通信問(wèn)題,這種情況議員們最多不批準(zhǔn)提案,不會(huì)影響一致性問(wèn)題。
死循環(huán)問(wèn)題
Paxos 算法有幾率出現(xiàn)死循環(huán)問(wèn)題,導(dǎo)致提案不通過(guò)。如下圖:
假設(shè)我們有2個(gè)議員同時(shí)發(fā)出提案請(qǐng)求。
- 議員1提交編號(hào)為1的提案并收到過(guò)半響應(yīng)。
- 此時(shí)議員2提交編號(hào)為2的提案也收到過(guò)半響應(yīng)。
- 由于提案階段響應(yīng)的編號(hào)已為2,根據(jù)“沒(méi)有對(duì)編號(hào)大于N的提案請(qǐng)求做出過(guò)響應(yīng),它就批準(zhǔn)該提案”,所以議員1的編號(hào)(1)在接收階段不會(huì)被批準(zhǔn)。
- 如果此時(shí)議員1重新發(fā)送編號(hào)為3的提案并通過(guò)后,議員2的提案在接收階段也不會(huì)通過(guò)。
如此循環(huán),就會(huì)造成死循環(huán)。
ZAB協(xié)議
ZAB 協(xié)議(ZooKeeper Atomic Broadcast)原子廣播是 ZooKeeper 用來(lái)保持所有服務(wù)器消息的順序同步并保證一致,與 Paxos 不同,其為主備架構(gòu),所以在同步數(shù)據(jù)時(shí)不會(huì)出現(xiàn)多個(gè)節(jié)點(diǎn)同時(shí)提交提案(死循環(huán)問(wèn)題),而是會(huì)在集群節(jié)點(diǎn)中選舉 Leader ** 節(jié)點(diǎn),統(tǒng)一經(jīng)由 Leader 節(jié)點(diǎn)進(jìn)行提案,但是主備架構(gòu)避免不了 Leader 節(jié)點(diǎn)的崩潰,如果出現(xiàn)該問(wèn)題,ZAB 還會(huì)保證集群節(jié)點(diǎn)的崩潰恢復(fù)**。關(guān)于ZAB更多描述去ZooKeeper官網(wǎng)看看。
所以 ZAB 協(xié)議主要做了3件事:
- 選舉 Leader 節(jié)點(diǎn)。
- 以廣播的方式保證副本之間的消息一致。
- Leader 節(jié)點(diǎn)崩潰后進(jìn)行崩潰恢復(fù)。
Leader 選舉
在這之前先了解下 ZAB 節(jié)點(diǎn)的三種狀態(tài):
- FOLLOWING:當(dāng)前節(jié)點(diǎn)是跟隨者,服從 leader 節(jié)點(diǎn)的命令。
- LEADING:當(dāng)前節(jié)點(diǎn)是 leader,負(fù)責(zé)協(xié)調(diào)事務(wù)。
- LOOKING:節(jié)點(diǎn)處于選舉狀態(tài)。
當(dāng)集群初始啟動(dòng)時(shí),每個(gè)節(jié)點(diǎn)會(huì)投自己一票并向其他節(jié)點(diǎn)發(fā)起投票請(qǐng)求,進(jìn)入 LOOKING 狀態(tài)。當(dāng)某個(gè)節(jié)點(diǎn)的投票數(shù)過(guò)半后,該節(jié)點(diǎn)進(jìn)入LEADING 狀態(tài),當(dāng)選 Leader,其他節(jié)點(diǎn)則會(huì)進(jìn)入 FOLLOWING 狀態(tài),成為 Follower。下面以3臺(tái)服務(wù)器為例,介紹 Leader 選舉過(guò)程:
發(fā)起投票
如上圖,三個(gè)節(jié)點(diǎn)同時(shí)啟動(dòng)首先會(huì)向自己投一票,并將(myid,ZXID)作為投票信息向其他兩個(gè)節(jié)點(diǎn)發(fā)送。此時(shí)每個(gè)節(jié)點(diǎn)的投票箱都是自己投個(gè)自己(myid,myid)。myid是每個(gè)節(jié)點(diǎn)的標(biāo)識(shí);ZXID 是最近一次的事務(wù)ID,初始值為0。
PK階段
每個(gè)節(jié)點(diǎn)在收到投票請(qǐng)求后會(huì)比較 ZXID,如果 ZXID 相等則比較 myid 。比較時(shí)如果自己節(jié)點(diǎn)的ZXID或myid小,那么更新自己的投票(myid,勝出節(jié)點(diǎn)id)并添加收到的投票至票箱(勝出節(jié)點(diǎn)id,勝出節(jié)點(diǎn)id)。
如上圖,node1 在收到 node2、3 的投票請(qǐng)求后,由于ZXID相等,node3的myid大,所以 node1 更新自己的投票箱并添加 node3 的投票,此時(shí)為(1,3)(3,3)。
node2同樣如此,投票結(jié)果為此時(shí)為(2,3)(3,3)。
node3不做更新操作。
廣播投票
node1、node2 在更新自己的投票結(jié)果后向其他兩個(gè)節(jié)點(diǎn)廣播投票結(jié)果,結(jié)果如上圖。
根據(jù)上述投票,三個(gè)服務(wù)器一致認(rèn)為 node3 應(yīng)該是 Leader 。所以 node3 進(jìn)入 LEADING 狀態(tài)成為 Leader,node1、node2 進(jìn)入 FOLLOWING 狀態(tài)稱(chēng)為 follower。
下圖是搭建了 Zookeeper 集群?jiǎn)?dòng)后的結(jié)果,也驗(yàn)證上述選舉算法。
廣播消息
為了保障副本之間的數(shù)據(jù)一致,ZAB協(xié)議做了以下幾點(diǎn):
- 所有的寫(xiě)請(qǐng)求都通過(guò) Leader 進(jìn)行操作,如果 Follower 收到寫(xiě)請(qǐng)求,會(huì)轉(zhuǎn)發(fā)給 Leader。
- Leader 針對(duì)寫(xiě)請(qǐng)求會(huì)生成一個(gè)提案,并為這個(gè)提案生成一個(gè)ZXID(保障一致、順序。)
- Followers 在收到提案后 ack 給 Leader。
- Leader 在收到過(guò)半的 Follower ack 后會(huì)廣播一個(gè) commit 消息。
- Follower 收到 commit 消息后會(huì)比較 ZXID,如果之前沒(méi)有處理過(guò)比 ZXID 大的寫(xiě)請(qǐng)求,那就進(jìn)行提交。
崩潰恢復(fù)
Leader 重新選舉
當(dāng)網(wǎng)絡(luò)異常造成網(wǎng)絡(luò)分區(qū)或者某個(gè)節(jié)點(diǎn)崩潰,如果是 Leader 節(jié)點(diǎn)這時(shí)候需要進(jìn)行重新選舉。如下圖
數(shù)據(jù)同步
選舉新的 Leader 后,其他節(jié)點(diǎn)的數(shù)據(jù)要與之同步。同步過(guò)程如下圖
- 在選舉為 Leader 后,node2 將自身的 Epoch 進(jìn)行自增并發(fā)送給 Follower,F(xiàn)ollower進(jìn)行更新并將自己的ZXID同步給 Leader 。每次選舉 Leader 后 Epoch 會(huì)+1,這樣,當(dāng)老的 Leader 重新連接到集群后,會(huì)對(duì)比其日志中 epoch 編號(hào)與 leader 此時(shí)的 epoch 編號(hào):對(duì)于 epoch 更小的那部分日志,就舍棄掉。
- Leader 根據(jù) ZXID 的差異進(jìn)行同步。上圖 Leader 會(huì)將 Follower 未提交的事務(wù)以提案進(jìn)行逐條發(fā)送和提交給 Follower ,F(xiàn)ollower 收到提案和提交請(qǐng)求后進(jìn)行同步。
老 Leader 恢復(fù)
當(dāng)老的 Leader 恢復(fù)后要加入集群中,其過(guò)程如下:
- node1 發(fā)起投票,node2、node3 響應(yīng)自己的角色和投票,通過(guò) node2 的響應(yīng),可以知道 node2 為 Leader ,并且通過(guò)兩者的投票可以確定 node2 為 Leader ,因此自己成為 Follower。
- 在選舉為 Leader 后 將自身的 Epoch 進(jìn)行自增并發(fā)送給 Follower,F(xiàn)ollower進(jìn)行更新并將自己的ZXID同步給 Leader。
- Leader 根據(jù) ZXID 的差異進(jìn)行同步。上圖 ZXID無(wú)差異,所以無(wú)需同步。
Raft算法
Raft 算法按照我的理解是和ZAB協(xié)議相似,兩者相同的概念可能名詞不同,比如:ZAB 中的 Epoch 和 Raft 的 Term 概念相同;實(shí)現(xiàn)方式大體相似,細(xì)節(jié)不同,比如:數(shù)據(jù)同步都是通過(guò) Leader 節(jié)點(diǎn)進(jìn)行提案,不同的是 Raft 通過(guò)狀態(tài)達(dá)到一致、Leader 選舉方式相似,發(fā)起投票時(shí)都會(huì)投自己一票,實(shí)現(xiàn)上 Raft 通過(guò)兩個(gè) timeout 控制選舉。這里我就不多廢話(huà)了,大家可以通過(guò)Raft算法動(dòng)態(tài)演示更容易理解。
總結(jié)
所以為什么有這么多的一致性定義呢?之間有什么關(guān)系?
我覺(jué)得還是場(chǎng)景,首先ACID、CAP、BASE都是理論,ACID是專(zhuān)注于事務(wù)、CAP理論作用在集群副本場(chǎng)景、BASE理論應(yīng)用最終一致性場(chǎng)景。
而2PC、3PC則是對(duì)于事務(wù)完整性的具體解決方案,Paxos、ZAB、Raft更傾向于集群副本一致性的解決方案。