作者 | bdseeker
來源 | BigData之路(ID:bigdata3186)
頭圖 | CSDN 下載自視覺中國
前言
上一篇《CAP》寫完之后,我又反復(fù)回看了多次,發(fā)現(xiàn)最后的一部分表達(dá)CAP、ACID、BASE、“BACP(自造)”關(guān)系時(shí)有一些問題,并且不是很嚴(yán)謹(jǐn),但是無奈已經(jīng)發(fā)送過的內(nèi)容,無法支持修改,并且有挺多小伙伴都在私聊我確認(rèn)細(xì)節(jié),這里我來重新更新下,直接上圖!
也有一部分小伙伴跟我說覺得CAP有點(diǎn)冷門,建議我換點(diǎn)大眾能接受的,其實(shí)我也不得不承認(rèn),大家可能更喜歡具體的技術(shù)棧。但我從個(gè)人視角來說,這些大數(shù)據(jù)的基礎(chǔ)知識(shí)非常重要。等我寫完大數(shù)據(jù)基礎(chǔ)系列,會(huì)逐漸開始轉(zhuǎn)戰(zhàn)具體技術(shù)棧,莫急!大數(shù)據(jù)之路很長,我們慢慢走。就像我每篇文章里都會(huì)提到的,相信堅(jiān)持總會(huì)有所收獲。
話不多說,進(jìn)入正題,在很多小伙伴認(rèn)知里或多或少都知道那么兩三種一致性,比如強(qiáng)一致性、最終一致性等。但是你以為一致性只有這些嘛?本篇帶來一致性詳解,講透一致性。歡迎大家指正~
強(qiáng)一致性
一致性大家庭中,雖然細(xì)分種類很多,但是實(shí)際上只有兩大類,其中之一就是上篇《CAP》中我們已經(jīng)介紹過的強(qiáng)一致性,其具體包含了嚴(yán)格一致性(也叫原子一致性或者線性一致性)和順序一致性。
1、嚴(yán)格(原子/線性)一致性
嚴(yán)格一致性代表著,當(dāng)數(shù)據(jù)更新后,所有Client的讀寫都是在數(shù)據(jù)更新的基礎(chǔ)上。如下圖所示,我們假設(shè)每份數(shù)據(jù)有三個(gè)副本,分別落到三個(gè)節(jié)點(diǎn)上。當(dāng)Client1嘗試將X的值置為1時(shí),嚴(yán)格一致性要求當(dāng)Client1完成更新操作以后,所有Client都要在最新值的基礎(chǔ)上進(jìn)行讀寫,這里的Client10讀取到的值是x=1,在同一時(shí)刻Client100的更新操作也是在x=1的基礎(chǔ)上進(jìn)行x+=1操作,在下一個(gè)時(shí)刻Client1000讀到的任意一個(gè)副本,X的值都會(huì)是2。
此時(shí)你會(huì)發(fā)現(xiàn)一切似乎都是很完美的。但是仔細(xì)想想,嚴(yán)格一致性的背后有什么潛臺(tái)詞呢?
數(shù)據(jù)同步復(fù)制
嚴(yán)格一致性代表著,所有數(shù)據(jù)在寫入操作的時(shí)候是同步復(fù)制的,即寫多副本都成功,才算寫入成功,HDFS是不是就是最好的例子。并且具有原子性,對(duì)于寫入操作來說,結(jié)果要么寫成功,要么寫失敗,不存在中間狀態(tài)。這也是為什么稱為原子一致性的原因。
嚴(yán)格一致性不考慮客戶端
在網(wǎng)上有很多人在解釋一致性時(shí),嘗試從客戶端和服務(wù)端分別解析;但是在上一篇我們分析CAP的時(shí)候,也有提到不要帶Client玩,那么究竟誰對(duì)誰錯(cuò)呢?
這里我們?cè)賮砜聪拢?strong>在考慮分布式系統(tǒng)的一致性時(shí)我們更關(guān)注什么?是多個(gè)Client發(fā)送讀寫請(qǐng)求到達(dá)后端的時(shí)間和先后順序嘛?不,我們真正關(guān)注的是每個(gè)請(qǐng)求,對(duì)應(yīng)服務(wù)端完成時(shí)間點(diǎn)的先后順序。還是參考上面的例子,Client1000讀取到x=2這個(gè)結(jié)果,實(shí)際上是以Client100這個(gè)寫操作完成為基礎(chǔ)的,如果Client100寫操作一直不完成,那么強(qiáng)一致性要求Client1000讀取到的X是1而不是2。因此我們更需要關(guān)注的是完成操作的具體時(shí)間點(diǎn),而不是操作發(fā)起的時(shí)間點(diǎn),對(duì)于一致性來說考慮Client的意義就不大了。當(dāng)然在同一時(shí)刻多個(gè)Client操作的冪等性還是一定要保證的。
換個(gè)角度,Client才是一致性需求的甲方,不是嘛。而分布式系統(tǒng)端作為乙方,只能滿足甲方需求,或者拒絕甲方需求,而不是要求甲方作出任何改變!
基于嚴(yán)格的全局時(shí)鐘
上面我們提到操作行為完成時(shí)間點(diǎn)的順序是十分重要的。再仔細(xì)看一下上面舉例的內(nèi)容,相信你會(huì)發(fā)現(xiàn)一切的行為都在時(shí)間這個(gè)維度上,行為順序是:Client1更新x=1 -> Client10讀取x=1/Client100在x=1的基礎(chǔ)上更新x+=1 -> Client1000讀取x=2。所以每個(gè)操作都是在前一個(gè)操作完成的基礎(chǔ)上進(jìn)行的,在分布式服務(wù)中,需要有一個(gè)基準(zhǔn)時(shí)間來衡量每個(gè)操作行為的順序。此時(shí)你會(huì)問了,機(jī)器上不是都有NTP做時(shí)間校準(zhǔn)嘛?現(xiàn)在的問題就是無法保證每一臺(tái)機(jī)器的時(shí)間都是絕對(duì)相同的。
舉個(gè)例子,數(shù)據(jù)D2所在的節(jié)點(diǎn)相比D1節(jié)點(diǎn)時(shí)間提前了幾秒,當(dāng)Client1的更新請(qǐng)求完成后(用時(shí)500ms),Client10的請(qǐng)求開始執(zhí)行并完成,如果將機(jī)器時(shí)間作為基準(zhǔn)就會(huì)發(fā)現(xiàn)Client10的讀取操作竟然在Client1的更新操作之前,這顯然是違背強(qiáng)一致性的。
我們一般我們會(huì)如何保證全局時(shí)鐘?這里簡單聊聊三種種常見解法。
混合邏輯時(shí)鐘 Hybrid Logic Clock
在混合邏輯時(shí)鐘中同時(shí)比較了節(jié)點(diǎn)本地的物理時(shí)間、邏輯時(shí)間和其他節(jié)點(diǎn)發(fā)送消息中的物理時(shí)間。kudu和Cockroachdb也都是使用的這個(gè)方法,HLC雖然加上了物理時(shí)間,但是仍然強(qiáng)依賴于機(jī)器的NTP,并不是嚴(yán)格意義上精確的時(shí)鐘,在HLC中需要為時(shí)鐘定義一個(gè)邊界,比如kudu中定義了maximum check error(最大時(shí)鐘錯(cuò)誤),如果本地NTP沒啟動(dòng),kudu在啟動(dòng)的時(shí)候就直接失敗了;如果誤差超過了maximum check error,依舊會(huì)報(bào)錯(cuò),這也就意味著當(dāng)超過HLC所設(shè)定的偏差邊界,HLC就不能正常工作了。
在看HLC的實(shí)現(xiàn)邏輯時(shí),發(fā)現(xiàn)步驟比較多,邏輯時(shí)間存在的意義就是在時(shí)間比對(duì)時(shí),當(dāng)作中間值或者備份值。這里由于不是本篇重點(diǎn),不再贅述了,感興趣的小伙伴可以看下論文:https://cse.buffalo.edu/tech-reports/2014-04.pdf。
True Time
上一篇文章中也有講到,谷歌依賴強(qiáng)大的基建實(shí)力降低了網(wǎng)絡(luò)分區(qū)發(fā)生的概率,而面全局時(shí)鐘問題,google的Spanner采用的是GPS + Atomic Clock(原子時(shí)鐘的含義可以百度一下)這種純硬件方式來對(duì)集群的機(jī)器進(jìn)行校時(shí),其精度在ms級(jí)別,這里我們用ε來表示時(shí)間精度的誤差,時(shí)間的精度誤差的范圍也就是[t-ε,t+ε]這個(gè)范圍之間。此時(shí)回到上面的操作中,按照此種方式Client10的機(jī)器時(shí)間相比Client1的機(jī)器時(shí)間最多提前或者滯后2個(gè)ε的時(shí)間,因此Spanner引入了commit wait time這個(gè)方案,說白了就是操作執(zhí)行完成后多等一會(huì),等過了這個(gè)精度誤差的范圍自然就全局有序了,Google將精度誤差控制在幾ms級(jí)別,當(dāng)然對(duì)于Spanner這種全球性、跨地域的分布式系統(tǒng)來說,多等個(gè)幾ms問題也不大。
但很遺憾,Google的這套硬件解決方案,并沒有開源出來,適用性有限,我們就望梅止渴吧。
授時(shí)中心 TimeStamp Oracle
在生活中也有”授時(shí)中心“的存在,貌似在陜西,具體位置可以查查,他的作用是什么呢?為中國各種基建、系統(tǒng)提供了一個(gè)準(zhǔn)確的時(shí)間,避免誤差。(個(gè)人YY:萬一打起仗,總不能因?yàn)槠渌麌腋蓴_了基準(zhǔn)時(shí)間,咱們所有基建就癱瘓吧,因此授時(shí)中心的意義巨大。)
在分布式服務(wù)中,實(shí)際上也有類似的方案。這里以Tidb舉例,Tidb為了校準(zhǔn)時(shí)間,就是采用了TSO這個(gè)方案,對(duì)于Tidb來說所有行為事件統(tǒng)一由PD節(jié)點(diǎn)分配時(shí)間,雖然這種方案會(huì)產(chǎn)生非常高頻的互相調(diào)用,但是按照Tidb官方介紹,在同IDC網(wǎng)絡(luò)環(huán)境下網(wǎng)絡(luò)傳輸開銷非常低,只有0.xms。當(dāng)然如果面對(duì)跨IDC的網(wǎng)絡(luò),就可以嘗試將PD節(jié)點(diǎn)和Tidb節(jié)點(diǎn)混部(Tikv依然需要獨(dú)立部署,為的是存儲(chǔ)計(jì)算分離)。這就不需要走網(wǎng)絡(luò)的開銷了,當(dāng)然如果是Client端跨IDC的話,還是沒有太好的方法。
2、順序一致性
上面我們說到了嚴(yán)格一致性(線性/原子),想做到全局時(shí)鐘下的全局絕對(duì)有序是有難度的,HLC實(shí)現(xiàn)比較復(fù)雜,谷歌的原子鐘+GPS又沒有開源出來,TSO又增加了系統(tǒng)的復(fù)雜度。想實(shí)現(xiàn)全局時(shí)鐘好難!
這里我們是否可以退一步,舍棄“時(shí)間”這個(gè)有序的計(jì)數(shù)器,嘗試構(gòu)造一個(gè)更好維護(hù)的計(jì)數(shù)器,不保證全局行為絕對(duì)有序,只保證分布式服務(wù)全局相對(duì)有序?
如下圖所示,D1先后更新了x=1,x=2,D3先后更新了a=1,a=2。當(dāng)Client讀取到D2節(jié)點(diǎn)時(shí),按照順序一致性要求,所有節(jié)點(diǎn)的操作相對(duì)順序都是相同的,一定是x=1在x=2之前,a=1在a=2之前,下圖舉例的是順序一致性的其中一種情況。
邏輯時(shí)鐘 Logic Clock
邏輯時(shí)鐘Logic Clock,這個(gè)名字你陌生的話,或許他的另一個(gè)名字Lamport Timestamp會(huì)讓你浮想連連,如果你還是沒啥印象的話,那么Paxos你是否知道呢?(如果做大數(shù)據(jù)的你不知道paxos,那你需要好好補(bǔ)習(xí)下基礎(chǔ)了