分布式系統(tǒng)的一致性算法------《Designing Data-Intensive Applications》讀書筆記13

一致性算法是分布式系統(tǒng)中最重要的問題之一。表面上看,這似乎很簡單,只是讓幾個節(jié)點在某些方面達成一致。在本篇之中,會帶大家完整的梳理分布式系統(tǒng)之中的共識算法,來更加深刻的理解分布式系統(tǒng)的設(shè)計。

1.原子提交和兩階段提交(2PC)

原子提交防止了數(shù)據(jù)庫處于半更新的狀態(tài),這對于需要滿足多對象事務(wù)和維護次級索引的數(shù)據(jù)庫尤為重要。每個次級索引都是從主數(shù)據(jù)中分離出來的數(shù)據(jù)結(jié)構(gòu),因此,如果修改某些數(shù)據(jù),也需要在次級索引中做出相應(yīng)的更改。通過原子性保證二級索引能夠與原數(shù)據(jù)保持一致。

分布式系統(tǒng)下的原子提交

我們先來看看,在一個單一的節(jié)點上是如何實現(xiàn)原子提交的:
對于執(zhí)行在一個單一數(shù)據(jù)庫節(jié)點的事務(wù),當客戶端向數(shù)據(jù)庫提交事務(wù)時,數(shù)據(jù)庫首先將事務(wù)信息添加到磁盤上的日志進行提交。如果數(shù)據(jù)庫在這個過程中間崩潰,則在節(jié)點重新啟動時從日志中恢復(fù)事務(wù)。如果在崩潰前將提交記錄成功寫入磁盤,則認為事務(wù)被提交,如果沒有,則該事務(wù)的任何寫入都回滾。在單一的節(jié)點的數(shù)據(jù)庫下,事務(wù)的原子提交取決于持久化寫入磁盤的順序,使得事務(wù)的提交原子化。

如果事務(wù)中涉及多個節(jié)點呢?情況就變得十分復(fù)雜了:

  • 有些節(jié)點可能檢測到約束違反或沖突,需要中止,而其他節(jié)點能夠成功地提交。

  • 一些提交請求可以在網(wǎng)絡(luò)中丟失,最終中止由于超時,而其他提交請求獲得通過。

  • 某些節(jié)點可能在提交記錄完全寫入和回滾恢復(fù)之前崩潰,而另一些節(jié)點則成功提交。

所以,一個節(jié)點必須確定事務(wù)在所有其他節(jié)點也要提交時才能進行提交。我們必須要有一種算法能夠讓分布式節(jié)點達成共識。

兩階段提交(2PC)。

兩階段提交協(xié)議就是我們要談到的第一個分布式共識協(xié)議。如下圖所示

  • 通過一個協(xié)調(diào)器,當應(yīng)用程序準備提交時,協(xié)調(diào)器節(jié)點開始的第1階段。它向每個參與的數(shù)據(jù)庫節(jié)點發(fā)送一個準備請求,詢問它們是否能夠提交?然后協(xié)調(diào)器跟蹤參與者的響應(yīng),如果所有參與者都能回答:是,表示他們準備提交,那么協(xié)調(diào)器在第2階段發(fā)出一個提交請求,則所有參與者同時進行提交,如果任何參與者回答:否,則協(xié)調(diào)器向第2階段的所有節(jié)點發(fā)送一個中止請求。
一個成功提交的兩階段協(xié)議

兩階段提交的問題

一旦出現(xiàn)了網(wǎng)絡(luò)故障或參與者失效,協(xié)調(diào)器節(jié)點可以通過超時機制來中止事務(wù)。二如果在階段二出現(xiàn)提交或中止事務(wù)失敗,協(xié)調(diào)器節(jié)點可以無限重試直到故障恢復(fù)。但是,如果這個過程之中協(xié)調(diào)器節(jié)點崩潰了,將會產(chǎn)生許多頭疼的問題:

如果協(xié)調(diào)器在發(fā)送準備請求之前失敗,參與者可以安全地中止事務(wù)。但是,一旦參與者收到了一個準備請求并回答了:是,它就必須等待從協(xié)調(diào)器節(jié)點的指令,事務(wù)是被提交還是中止。而一旦協(xié)調(diào)器節(jié)點崩潰或出現(xiàn)網(wǎng)路故障,參與者只能無限期的等待。如下圖所示:

Database1陷入無限等待之中,所有資源被鎖住

由于協(xié)調(diào)器在將提交請求發(fā)送到Database 1之前崩潰了,因此Database 1不知道事務(wù)是否提交。如果數(shù)據(jù)庫1單方面中止暫停之后,它的結(jié)果與Database 2產(chǎn)生不一致。 唯一的辦法是等待協(xié)調(diào)器恢復(fù),這就是為什么在向參與者提交或中止事務(wù)請求之前,協(xié)調(diào)器必須將其提交或中止的結(jié)果寫入本地磁盤上的事務(wù)日志。當協(xié)調(diào)器恢復(fù)時,它通過讀取其事務(wù)日志確定所有可疑事務(wù)的狀態(tài)。任何在協(xié)調(diào)器日志中沒有提交記錄的事務(wù)都會被中止。

2.協(xié)商一致性

由上文我們可以了解,在分布式系統(tǒng)之中可以使用兩階段提交協(xié)議來實現(xiàn)的事務(wù)(也可以使用兩階段提交協(xié)議的升級版三階段提交協(xié)議)。分布式事務(wù)雖然解決了分布式數(shù)據(jù)庫之中的事務(wù)實現(xiàn),但是它也引入了新的問題,事務(wù)協(xié)調(diào)器本身也是一種數(shù)據(jù)庫(在其中存儲事務(wù)的結(jié)果)。如果協(xié)調(diào)器只在一臺機器上運行,很容易就會產(chǎn)生單點故障問題。而默認情況下,許多協(xié)調(diào)器實現(xiàn)不是高可用的,只有基本的副本備份功能。許多服務(wù)器端應(yīng)用程序以無狀態(tài)模型為基礎(chǔ),將持久性狀態(tài)都存儲在數(shù)據(jù)庫中,其優(yōu)點是可以隨意添加和刪除應(yīng)用服務(wù)器。但是,當協(xié)調(diào)器成為應(yīng)用程序服務(wù)器的一部分時,它會改變部署的性質(zhì)。因為協(xié)調(diào)器的日志同樣成為了需要持久化狀態(tài)存儲的內(nèi)容。一方面,分布式事務(wù)很難實現(xiàn)完整的一致性保障。另一方面,由于協(xié)調(diào)器節(jié)點的存在,分布式事務(wù)會大大降低數(shù)據(jù)庫的性能,所以很多數(shù)據(jù)系統(tǒng)放棄了對分布式事務(wù)的支持。

協(xié)商一致性

其實分布式事務(wù)困難點就在于實現(xiàn)一致性,它要求系統(tǒng)之中多個節(jié)點達成共識。例如,如果有幾個人同時嘗試預(yù)訂飛機上的最后一個座位或同一個劇院的座位,或者試圖用相同的用戶名注冊一個帳戶,則可以使用一致性算法來最終確定哪種哪個操作是成功的,并且在分布式系統(tǒng)的節(jié)點之中達成一致的共識。

而協(xié)商一致性通常的形式如下:一個或多個節(jié)點可以提出取值,而協(xié)商一致性算法決定其中一個值。 協(xié)商一致性的核心理念:每個人都決定相同的結(jié)果,一旦你決定了,你就不能改變你的想法。我們可以先簡化這個模型,摒棄容錯性:可以指定一個節(jié)點成為Leader,由Leader節(jié)點做出所有的決定。但是,如果Leader節(jié)點失效,則系統(tǒng)陷入癱瘓。兩階段提交協(xié)議之中的協(xié)調(diào)器就是一個Leader,一旦協(xié)調(diào)器失效了,系統(tǒng)無法進行工作。而更好的協(xié)商一致性算法要求,即使某些節(jié)點失效了,系統(tǒng)仍然能夠正常工作。當然,如果所有節(jié)點都崩潰了,并且沒有一個節(jié)點正在運行,那么任何算法都不可能雞血運行,所以說算法可以容忍的故障數(shù)量是有限的:事實上,可以證明任何協(xié)商一致算法至少需要大多數(shù)節(jié)點正常運行,來確保協(xié)商一致。

一致性算法與全序廣播

在分布式系統(tǒng)之中存在許多協(xié)商一致性算法算法如:Paxos,Raft,Zab等等。本篇之中,不會涉及到不同算法的全部細節(jié),會通過他們來了解一些高級思想。(后續(xù)大家有興趣會給大家來詳解這些算法

這里的一致性算法符合全序廣播的特性,全序廣播需要以相同的順序向所有節(jié)點精確地傳遞一次消息。在每一輪的協(xié)商之中,每個節(jié)點都可以提出下一個要發(fā)送的消息,然后由協(xié)商達成一致,并在系統(tǒng)之中傳遞的下一條消息。所有節(jié)點共同決定以相同的順序傳遞相同的消息,且消息不重復(fù),消息不會被破壞,也不會憑空產(chǎn)生。(這里忽略拜占庭問題,如果需要引入拜占庭容錯,需要采用類似于區(qū)塊鏈之中的Pow算法)

epoch編號和Leader選舉

每一輪的消息都進行協(xié)商,是缺乏效率的行為。所以類似于Raft與Zab等協(xié)議都利用Leader機制來進行優(yōu)化。所以協(xié)商一致協(xié)議需要選舉出Leader,并且保證Leader是獨一無二的。如何來保證分布式節(jié)點對Leader節(jié)點的選舉結(jié)果的共識呢?這里利用分布式的時序機制,每一個通過合法選舉出的Leader存在一個epoch編號,來確保Leader是獨特的。 一旦Leader節(jié)點失效,則各個節(jié)點之間開始投票選出新的Leader,新的Leader會產(chǎn)生一個新的epoch值,所以epoch值是根據(jù)Leader選舉順序單調(diào)遞增的。如果兩個不同的Leader在發(fā)生沖突(也許是因為前一個領(lǐng)導(dǎo)人實際上并沒有死),那么所有節(jié)點會認同更高的epoch值。

Leader需要做出的每一個決策,它都必須將提議的值發(fā)送給其他節(jié)點,并等待節(jié)點的集合來響應(yīng)提議。此時協(xié)商一致需要達到法定人數(shù),也就是組成集群的大多數(shù)節(jié)點。一個節(jié)點會投票贊成一個提案,只有當它不知道任何其他Leader具有更高的epoch值。協(xié)商一致性分為兩個運行階段:一:選擇一個Leader,二:投票表決Leader的提議。當Leader收到法定人數(shù)的投票結(jié)果同意提案,則可以斷定,沒有一個具有更高epoch值的Leader,然后它可以安全地提交結(jié)果。(可以用反證法證明) 這個投票過程雖然看起來類似于兩階段提交。最大的差異是協(xié)調(diào)器節(jié)點的容錯,來保證協(xié)商一致算法高度可用性。

3.協(xié)商一致性的代價

協(xié)商一致性算法是分布式系統(tǒng)的一個重大突破:它給所有其他不確定的系統(tǒng)帶來了具體的解決方案,并且它仍然是容錯的(只要大多數(shù)節(jié)點都能正常工作),并且提供全序廣播的特性,因此他們也可以在容錯的方式實現(xiàn)線性化的原子操作。

天下沒有免費的午餐,這種好處是有代價的。

  • 協(xié)商過程之中需要對提案進行表決,而這個表決的過程本質(zhì)上是一種同步復(fù)制。前文我們已經(jīng)探討過同步復(fù)制與異步復(fù)制的優(yōu)缺點。而在實踐之中,我們通常配置使用異步復(fù)制。某些提交的數(shù)據(jù)可能在故障轉(zhuǎn)移時丟失,但為了更好的性能,許多人選擇接受這種風險。

  • 協(xié)商一致性嚴格要求需要多數(shù)決。這意味著你需要至少三個節(jié)點來容忍一個故障(剩下的兩個組成多數(shù)),或者至少五個節(jié)點來容忍兩個故障(剩下的三個組成多數(shù))。

  • 大多數(shù)協(xié)商一致算法假設(shè)一組固定的節(jié)點參與投票,這意味著不能動態(tài)的添加或刪除集群中的節(jié)點。

  • 如何檢測失效的節(jié)點也是一個問題。在具有高度可變網(wǎng)絡(luò)延遲的環(huán)境中,經(jīng)常發(fā)生一個節(jié)點錯誤地認為Leader的失效。雖然這個錯誤不會損害系統(tǒng)的安全性,但是頻繁的Leader選舉會導(dǎo)致系統(tǒng)糟糕的表現(xiàn),因為系統(tǒng)最終會花費更多的時間去選擇一個領(lǐng)導(dǎo)者而不是做任何有用的工作。

所以設(shè)計對不可靠網(wǎng)絡(luò)更健壯的算法仍然是我們計算機工作者需要挑戰(zhàn)和研究的問題。

小結(jié):

從兩階段提交協(xié)議起步,我們梳理了分布式系統(tǒng)下的一致性算法,并且分析了它們的優(yōu)劣。通過這些外包出去的分布式協(xié)調(diào)服務(wù)作為基礎(chǔ)模型,我們才能實現(xiàn)更多,更加復(fù)雜的分布式服務(wù)。

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

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

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