客戶端一致性與多Leader機(jī)制------《Designing Data-Intensive Applications》讀書筆記7

接著上一篇的內(nèi)容,我們繼續(xù)來梳理分布式系統(tǒng)之中的副本機(jī)制與副本一致。上文我們聊到了在可用性與一致性之間的一個(gè)折中的一致性等級(jí):最終一致性。我們順著上篇的內(nèi)容,由用戶來分析一致性等級(jí)。

1. 客戶端的困擾

上篇文章我們提到了數(shù)據(jù)系統(tǒng)常用的模型,當(dāng)提交新數(shù)據(jù)時(shí),必須將它發(fā)送給Leader節(jié)點(diǎn),但是當(dāng)用戶查詢數(shù)據(jù)時(shí),可以從一個(gè)Follower節(jié)點(diǎn)讀取該數(shù)據(jù)。
這樣的模型使十分適合Web應(yīng)用的讀多寫少的特點(diǎn)。

讀寫一致性

但是倘若Leader與Follower之間以異步的方式復(fù)制的話,會(huì)存在一些問題。如下圖所示:如果用戶數(shù)據(jù)剛剛寫入,而新的數(shù)據(jù)可能尚未達(dá)到Follower節(jié)點(diǎn)的副本。在用戶的角度,他們提交的數(shù)據(jù)看起來似乎丟失了。

用戶無法讀取到新寫入的數(shù)據(jù)

在這種情況下,我們需要讀寫一致性。對(duì)于用戶來說,總是能看到它們最新更新的數(shù)據(jù)。而其他用戶的更新可能需要一定的時(shí)間之后才可見?,F(xiàn)在新的問題來了,我們?nèi)绾螌?shí)現(xiàn)Leader-Follower機(jī)制下的讀寫一致性呢?

這里有一個(gè)最簡(jiǎn)單粗暴的規(guī)則是:用戶可以選擇總是從Leader節(jié)點(diǎn)那里讀取自己寫入的數(shù)據(jù),然后選擇自從Follower節(jié)點(diǎn)處讀取其他用戶寫入的數(shù)據(jù)。(注:這里的技巧十分巧妙,十分適合在多用戶下的隔離,但是僅僅適用于每個(gè)用戶都僅僅修改自己數(shù)據(jù)的場(chǎng)景。)

所以更好的方式是時(shí)間戳機(jī)制,客戶端可以通過記錄最近一次寫入的時(shí)間戳,然后數(shù)據(jù)系統(tǒng)需要確保為該用戶提供的任何讀取的副本至少在該時(shí)間戳之后更新。如果一個(gè)副本還沒有達(dá)到最新的時(shí)間戳,則該讀取需要由另一個(gè)副本處理,或者等待可本節(jié)點(diǎn)的副本跟進(jìn)到滿足要求的時(shí)間戳。時(shí)間戳可以是邏輯時(shí)間戳(表示寫入順序的命令,如日志序列號(hào))或?qū)嶋H系統(tǒng)時(shí)鐘(強(qiáng)依賴系統(tǒng)時(shí)鐘的話,需要處理時(shí)鐘回?fù)艿葐栴},十分麻煩~~~)。

單調(diào)讀一致性

解決了讀寫一致性,我們?cè)賮砜纯聪旅娴倪@個(gè)場(chǎng)景:


單調(diào)讀時(shí)在不同副本的幻讀現(xiàn)象

因?yàn)橛脩艨梢詮亩鄠€(gè)不同的副本進(jìn)行多次讀取,則可能發(fā)生這種情況。如上圖所示,用戶2345進(jìn)行了兩次相同的查詢,第一次訪問了Follower1節(jié)點(diǎn),第二次查詢?cè)L問了Follower2節(jié)點(diǎn)。第一次查詢返回一個(gè)最近由用戶1234添加的注釋,但是第二次查詢并沒有上次查詢的注釋了,因?yàn)闇蟮腇ollower還沒有同步到之前的寫入注釋的操作。如果用戶2345第一次看到用戶1234的注釋出現(xiàn),然后再次查詢它時(shí)卻消失了,這對(duì)用戶2345來說是非常令人困惑的。

單調(diào)讀一致性來是保證這種異常不會(huì)發(fā)送。當(dāng)客戶讀到的數(shù)據(jù),保證不會(huì)看到一個(gè)舊的數(shù)據(jù)。要滿足單調(diào)讀一致性。實(shí)現(xiàn)單調(diào)讀取的一種方法是確保每個(gè)用戶總是從同一副本中讀?。ú煌挠脩艨梢詮牟煌母北咀x?。@纾梢愿鶕?jù)用戶ID散列選擇副本,而不是隨機(jī)選擇。

多數(shù)據(jù)中心下的交叉設(shè)備讀
在多個(gè)數(shù)據(jù)中心的環(huán)境下,問題會(huì)變的更加復(fù)雜。任何需要由Leader節(jié)點(diǎn)服務(wù)的請(qǐng)求都必須路由轉(zhuǎn)發(fā)到包含Leader的數(shù)據(jù)中心。當(dāng)同一用戶從多個(gè)設(shè)備訪問服務(wù)時(shí),另一個(gè)復(fù)雜的問題出現(xiàn)了,例如桌面Web瀏覽器和移動(dòng)應(yīng)用程序。在這種情況下,您可能希望在讀寫一致性的基礎(chǔ)之上提供交叉設(shè)備讀:如果用戶輸入某個(gè)設(shè)備上的一些信息,然后在另一個(gè)設(shè)備上查看,則應(yīng)該看到他們剛剛輸入的信息。如果需要提供交叉設(shè)備讀,記錄用戶上次更新的時(shí)間戳是十分困難的,因?yàn)橐粋€(gè)設(shè)備不知道其他設(shè)備上發(fā)生了什么更新。假如你的副本分布在不同的數(shù)據(jù)中心,也不能保證不同設(shè)備的連接將被路由引導(dǎo)到同一數(shù)據(jù)中心。

小結(jié):當(dāng)使用一個(gè)最終一致性的數(shù)據(jù)系統(tǒng)時(shí),如果復(fù)制延遲增加到幾分鐘甚至幾小時(shí),就需要考慮應(yīng)用程序的行為。如果答案是“沒有問題”,那太好了。但如果應(yīng)用程序?qū)σ恢滦悦舾?,則應(yīng)用程序需要提供額外的處理邏輯來處理特殊的場(chǎng)景,如對(duì)某些特殊的讀取操作,可以限定只對(duì)Leader節(jié)點(diǎn)執(zhí)行某些類型的讀取。但是,在應(yīng)用程序代碼中處理這些問題會(huì)很復(fù)雜,很容易出錯(cuò)。事務(wù)確保了許多一致性模型,使應(yīng)用程序更簡(jiǎn)單。然而,在向分布式的環(huán)境之中,許多數(shù)據(jù)系統(tǒng)放棄了對(duì)事務(wù)的支持,因?yàn)槭聞?wù)會(huì)大大降低分布式環(huán)境之中系統(tǒng)的性能與可用性。所以,最終一致性能夠使用的場(chǎng)景有限,我們還是要按需選擇,避免踩坑。

2. 多Leader機(jī)制

在多數(shù)據(jù)中心的環(huán)境下,如果僅僅只有一個(gè)Leader,所以每次寫操作都必須訪問同一個(gè)數(shù)據(jù)中心,這將會(huì)導(dǎo)致延遲大大提高。所以我們可以考慮多Leader的機(jī)制。在多Leader機(jī)制可以在每個(gè)數(shù)據(jù)中心中設(shè)置一個(gè)Leader。下圖展示了多Leader機(jī)制的結(jié)構(gòu):

多Leader機(jī)制的結(jié)構(gòu)

在數(shù)據(jù)中心內(nèi)部,保持前文提到的Leader-Follower機(jī)制。而跨數(shù)據(jù)中心的Leader之間通過沖突協(xié)調(diào)進(jìn)行數(shù)據(jù)同步。我們來梳理一下多Leader機(jī)制的一些特點(diǎn)

  • 性能

在多Leader機(jī)制中,每個(gè)寫操作可以在本地?cái)?shù)據(jù)中心進(jìn)行處理,再異步復(fù)制到其他的數(shù)據(jù)中心。因此可以大大降低跨數(shù)據(jù)中心的網(wǎng)絡(luò)延遲,性能表現(xiàn)顯然會(huì)更好。

  • Leader失效

在單Leader的機(jī)制里,如果數(shù)據(jù)中心失效,則故障轉(zhuǎn)移可以使另一個(gè)數(shù)據(jù)中心中的Follower成為L(zhǎng)eader。而多Leader機(jī)制,每個(gè)數(shù)據(jù)中心可以獨(dú)立于其他數(shù)據(jù)中心繼續(xù)運(yùn)行。

  • 網(wǎng)絡(luò)的延遲與故障

數(shù)據(jù)中心之間的通信通常依托于公共互聯(lián)網(wǎng),它相比數(shù)據(jù)中心內(nèi)的本地網(wǎng)絡(luò)更加不可靠。顯然具有異步復(fù)制特性的多Leader機(jī)制可以更好地容忍跨數(shù)據(jù)中心通信的延遲與故障。

寫沖突

雖然多Leader機(jī)制具備了很多優(yōu)勢(shì),它也有一個(gè)大缺點(diǎn)是:相同的數(shù)據(jù)可以在兩個(gè)不同的數(shù)據(jù)中心,一旦數(shù)據(jù)同時(shí)被修改就必須要有機(jī)制來解決寫沖突的問題。如下圖所示,考慮一個(gè)同時(shí)由兩個(gè)用戶編輯的wiki頁(yè)面。User1將頁(yè)面標(biāo)題從A改為B,User2同時(shí)將標(biāo)題從A改為C。每個(gè)用戶的更改都分別成功提交給了Leader1與Leader2。當(dāng)進(jìn)行異步復(fù)制時(shí),系統(tǒng)會(huì)檢測(cè)到?jīng)_突:

同時(shí)更新同一條記錄而出現(xiàn)寫沖突

在一個(gè)單Leader的數(shù)據(jù)系統(tǒng)之中,User2要么阻塞,等待第一次寫入完成,要么中止第二個(gè)寫事務(wù),迫使User2重試寫入。而在多Leader機(jī)制之中,兩個(gè)寫入操作都是成功的,并且沖突只是在稍后的某個(gè)時(shí)間點(diǎn)異步檢測(cè)到的。有什么辦法可以解決這樣的問題呢?

  • 避免沖突
    避免沖突:如果應(yīng)用程序可以確保某個(gè)特定記錄的所有寫入都由同一個(gè)Leader處理,那么沖突就不會(huì)發(fā)生。由于多Leader機(jī)制處理沖突十分復(fù)雜,避免沖突是經(jīng)常推薦的方法。(在用戶可以編輯自己的數(shù)據(jù)的應(yīng)用程序中,可以確保特定用戶的請(qǐng)求總是路由到同一個(gè)數(shù)據(jù)中心,并使用該數(shù)據(jù)中心中的Leader處理讀寫請(qǐng)求。不過這只是一種鴕鳥策略,用戶地理位置的轉(zhuǎn)移,或者是路由系統(tǒng)的更新,沖突協(xié)調(diào)仍然不可避免。
  • 收斂到一致狀態(tài)
    在單Leader的機(jī)制中:如果對(duì)同一個(gè)字段有多個(gè)更新,最后一個(gè)寫入確定字段的最終值。。而在多Leader的機(jī)制中,沒有定義的寫入順序,因此不清楚最終值應(yīng)該是什么。所以數(shù)據(jù)系統(tǒng)必須以收斂的方式解決沖突,這意味著當(dāng)所有更改都被復(fù)制時(shí),所有副本必須到達(dá)相同的最終值。可以為每個(gè)寫操作分配一個(gè)唯一的ID(例如,一個(gè)時(shí)間戳,一個(gè)長(zhǎng)的隨機(jī)數(shù),一個(gè)UUID或散列的鍵和值),最高的ID值認(rèn)為是最終值,這種技術(shù)被稱為Last Write Win(LWW)。(強(qiáng)依賴系統(tǒng)時(shí)間又會(huì)造成很多問題,唉,這真的很煩

  • 自定義沖突消解的邏輯
    最合適的解決沖突的方法可能取決于應(yīng)用程序,該代碼可以在寫或讀時(shí)執(zhí)行:一旦數(shù)據(jù)系統(tǒng)檢測(cè)到復(fù)制更改日志中的沖突,它就調(diào)用沖突處理程序。或是在應(yīng)用程序讀取的階段檢測(cè)到?jīng)_突時(shí),會(huì)將這些數(shù)據(jù)的多個(gè)版本將返回應(yīng)用程序。應(yīng)用程序可以提示用戶或自動(dòng)解決沖突,并將結(jié)果寫入數(shù)據(jù)庫(kù)。(Cassandra與CouchDB就是采取了這種機(jī)制

多Leader機(jī)制的復(fù)制拓?fù)?/h4>

兩個(gè)Leader進(jìn)行同步時(shí),拓?fù)浣Y(jié)構(gòu)十分簡(jiǎn)單。但是一旦擴(kuò)展到4,5個(gè)Leader,之后多個(gè)Leader之間的同步結(jié)構(gòu)又應(yīng)該是怎么樣的呢?(雖然在實(shí)踐中,很少采用這樣的架構(gòu)

多Leader機(jī)制的拓?fù)浣Y(jié)構(gòu)

最一般的拓?fù)浣Y(jié)構(gòu)是圖(c),其中每個(gè)節(jié)點(diǎn)都將其寫入傳遞給所有的節(jié)點(diǎn)。而(a)或(b)采用了環(huán)形或星型的結(jié)構(gòu)來減少網(wǎng)絡(luò)的流量。在環(huán)形和星形拓?fù)渲?,在到達(dá)所有副本之前,寫入可能需要經(jīng)過幾個(gè)節(jié)點(diǎn)。因此,節(jié)點(diǎn)需要轉(zhuǎn)發(fā)它們從其他節(jié)點(diǎn)接收到的數(shù)據(jù)更改。為了防止無限復(fù)制循環(huán),每個(gè)節(jié)點(diǎn)都被賦予唯一的標(biāo)識(shí)符,并且在復(fù)制日志中,每個(gè)寫入都用它經(jīng)過的所有節(jié)點(diǎn)的標(biāo)識(shí)符標(biāo)記。當(dāng)一個(gè)節(jié)點(diǎn)接收一個(gè)帶有自己標(biāo)識(shí)符的數(shù)據(jù)更改時(shí),該數(shù)據(jù)更改將被忽略,因?yàn)楣?jié)點(diǎn)知道它已經(jīng)被處理了。

環(huán)形和星形結(jié)構(gòu)存在的一個(gè)問題是,如果有一個(gè)節(jié)點(diǎn)失效,會(huì)中斷其他節(jié)點(diǎn)之間的同步消息流,而因?yàn)樗辉试S消息沿著不同的路徑傳播,造成了單點(diǎn)故障。但是All pass的結(jié)構(gòu)也會(huì)帶來一些新的問題,由于網(wǎng)絡(luò)擁塞的原因,各個(gè)節(jié)點(diǎn)的信息接收順序不一致,如下圖所示:


寫入操作亂序到達(dá)

Client A將行插入到一個(gè)Leader 1的表,和Client B在Leader 3之中進(jìn)行更新。而Leader 2收到了不同順序的寫操作:update操作出現(xiàn)在了insert操作之前。為了正確地排列這些事件,我們可以使用一種稱為多版本向量控制(MVCC)的技術(shù)。至于什么是MVCC,我們下一篇繼續(xù)來梳理~~( 不是我故意賣關(guān)子啊,只是怕寫的太長(zhǎng)你們懶得看~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容