作者 | Sean Loiselle, Jessica Edwards
譯者 | 崔瑩峰
策劃 | 云昭
電腦可以沒日沒夜地運(yùn)行,但早先的網(wǎng)站卻做不到24*7小時的運(yùn)營。現(xiàn)在看來我們都不可思議。然而,在互聯(lián)網(wǎng)出現(xiàn)之前,24*7的高可用性這個提法并不存在。
那時我們對可用性是有期待,但我們卻壓根不會認(rèn)為這是我們有權(quán)要求獲得的東西。我們只在需要時使用計(jì)算機(jī);更多的時候我們其實(shí)本來就不太有機(jī)會使用它,自然也很少會讓計(jì)算機(jī)一直開著以便它可以隨時響應(yīng)我們。隨著互聯(lián)網(wǎng)的發(fā)展,那些以前在當(dāng)?shù)貢r間凌晨 3 點(diǎn)不常見的請求變得越來越多,也讓這個時間段在全球各地都變成了黃金營業(yè)時間,因此確保計(jì)算機(jī)能夠服務(wù)這些請求變得非常重要。
然而,許多系統(tǒng)僅依靠一臺計(jì)算機(jī)來處理這些請求——單點(diǎn)故障——我們都知道這是個很糟糕的事實(shí)。為了保持正常運(yùn)行,我們需要在多臺可以滿足我們需求的計(jì)算機(jī)之間分配負(fù)載。然而,分布式計(jì)算有著非常明顯的優(yōu)勢:特別是同步和容忍系統(tǒng)內(nèi)的部分故障(容錯)。每一代工程師都在迭代這些解決方案,以滿足這個時代的需求。
數(shù)據(jù)庫領(lǐng)域是如何引入分布式的,這件事很多人并不清楚前因后果,因?yàn)檫@本身就是一個比其他領(lǐng)域發(fā)展緩慢得多的難題。當(dāng)然,軟件在本地?cái)?shù)據(jù)庫中會記錄一些分布式計(jì)算的結(jié)果,但數(shù)據(jù)庫本身的狀態(tài)只能保存在單臺機(jī)器上。為什么?因?yàn)榭鐧C(jī)器的狀態(tài)復(fù)制是非常困難的。
在這篇文章中,我們來看看分布式數(shù)據(jù)庫在歷史上是如何處理容錯的,以及高可用性是什么樣子的。另外,我們還會介紹高可用性系統(tǒng)的幾種不同類型。
高可用性數(shù)據(jù)庫有哪些類型?
高可用性數(shù)據(jù)庫一般情況下可分為兩類,目前出現(xiàn)了第三類,而且變得越來越普遍:
主-從數(shù)據(jù)庫:數(shù)據(jù)庫有一個主節(jié)點(diǎn)處理請求,另外一個是熱備節(jié)點(diǎn)(即從節(jié)點(diǎn)),一旦主節(jié)點(diǎn)故障后就會投入使用
主-主數(shù)據(jù)庫:數(shù)據(jù)庫具有多個主節(jié)點(diǎn),這些節(jié)點(diǎn)將數(shù)據(jù)分片后分別對數(shù)據(jù)庫進(jìn)行寫操作
多主數(shù)據(jù)庫:數(shù)據(jù)庫具有至少三個主節(jié)點(diǎn),這些節(jié)點(diǎn)都可以對集群中的任何數(shù)據(jù)執(zhí)行讀寫操作而不會產(chǎn)生沖突。
什么是主從可用性?
主從可用性意味著數(shù)據(jù)庫有一個主節(jié)點(diǎn)處理請求,另外一個是熱備節(jié)點(diǎn)(即從節(jié)點(diǎn)),一旦主節(jié)點(diǎn)故障后就會投入使用。主從可用性模型基于兩節(jié)點(diǎn)概念,即一個節(jié)點(diǎn)接收所有請求,然后再將數(shù)據(jù)復(fù)制到其追隨者。
在過去,數(shù)據(jù)庫在單臺機(jī)器上運(yùn)行。它只有一個節(jié)點(diǎn),處理所有的讀寫操作。沒有所謂的“部分失敗”;數(shù)據(jù)庫不是成功啟動就是失敗停服。
對互聯(lián)網(wǎng)來說,單節(jié)點(diǎn)數(shù)據(jù)庫的完全不可用是一個雙重問題;首先,計(jì)算機(jī)全天候被訪問,因此停機(jī)更有可能直接影響用戶;其次,通過將計(jì)算機(jī)置于持續(xù)的訪問請求之下,它們更有可能出現(xiàn)故障。很明顯,這個問題的解決方案是使用多臺計(jì)算機(jī)來共同處理請求,這也是分布式數(shù)據(jù)庫起步的契機(jī)所在。
生活在單節(jié)點(diǎn)世界中,最自然的解決方案是繼續(xù)讓單個節(jié)點(diǎn)提供讀寫服務(wù),并將其狀態(tài)簡單地同步到輔助的備份機(jī)器上——因此,主從復(fù)制誕生了。
主從模式是通過即時備份實(shí)現(xiàn)高可用性的早期步驟。在主節(jié)點(diǎn)發(fā)生故障的情況下,您可以簡單地將流量引導(dǎo)到從節(jié)點(diǎn),從而將其提升為主節(jié)點(diǎn)。只要有可能,您就會用一臺新的備份機(jī)器替換停機(jī)的服務(wù)器(并希望主節(jié)點(diǎn)在此期間不會出現(xiàn)故障)。
首先,從主節(jié)點(diǎn)到從節(jié)點(diǎn)的復(fù)制是一個同步過程,即在從節(jié)點(diǎn)確認(rèn)之前,數(shù)據(jù)轉(zhuǎn)換并不會被提交。但是,目前還不清楚如果從節(jié)點(diǎn)出現(xiàn)故障該怎么辦。如果備份系統(tǒng)不可用,整個系統(tǒng)宕機(jī)當(dāng)然沒有意義——但是只要使用同步復(fù)制,就會發(fā)生這種情況。
為了進(jìn)一步提高可用性,可以改為異步復(fù)制數(shù)據(jù)。雖然它的架構(gòu)看起來是一樣的,但它能夠處理主節(jié)點(diǎn)或從節(jié)點(diǎn)的宕機(jī),而不會影響數(shù)據(jù)庫的可用性。
雖然異步主從模式是向前邁出的又一步,但仍然存在明顯的缺點(diǎn):
當(dāng)主節(jié)點(diǎn)宕機(jī)時,任何尚未復(fù)制到從節(jié)點(diǎn)的數(shù)據(jù)都可能丟失——即使客戶端已經(jīng)確認(rèn)了數(shù)據(jù)完全提交。
依靠單臺機(jī)器來處理流量,仍然受限于單臺機(jī)器的最大可用資源。.
對五個 9 的高可用性追求
擴(kuò)展到多臺機(jī)器
隨著互聯(lián)網(wǎng)的普及,業(yè)務(wù)需求的規(guī)模和復(fù)雜性都在增長。對于數(shù)據(jù)庫來說,這意味著它們需要能夠處理比任何單個節(jié)點(diǎn)都多的流量,并且提供“始終在線”的高可用性成為一項(xiàng)任務(wù)。
鑒于現(xiàn)在大量工程師擁有從事其他分布式技術(shù)的經(jīng)驗(yàn),很顯然,數(shù)據(jù)庫可以超越單節(jié)點(diǎn)的主從模式,將數(shù)據(jù)庫分布在多臺機(jī)器上。
分片
同樣,我們可以從調(diào)整現(xiàn)有的系統(tǒng)起步,我們的工程師通過開發(fā)分片將主動復(fù)制調(diào)整為更具可擴(kuò)展性的架構(gòu)。
在此方案中,您按某個值(例如行數(shù)或主鍵中的唯一值)拆分集群的數(shù)據(jù),并將這些段分布在多個實(shí)例,每個實(shí)例都有一套主從節(jié)點(diǎn)。然后,您在由這些實(shí)例組成的集群前添加某種路由技術(shù),以將客戶端請求引導(dǎo)到正確的實(shí)例來處理。
分片允許您在將工作負(fù)載分配到多臺機(jī)器上,從而提高吞吐量,并通過容忍更多的部分故障和消除單點(diǎn)故障來創(chuàng)建更大的彈性。
盡管有這些好處,但對系統(tǒng)進(jìn)行分片是復(fù)雜的,并且給團(tuán)隊(duì)帶來了巨大的運(yùn)維負(fù)擔(dān)。特意對碎片進(jìn)行的統(tǒng)計(jì)可能會變得非常繁瑣,以至于路由最終會滲入應(yīng)用程序的業(yè)務(wù)邏輯。更糟糕的是,如果您需要修改系統(tǒng)分片的方式(例如模式更改),通常需要明顯的(甚至是巨大的)工程量來實(shí)現(xiàn)。
單節(jié)點(diǎn)主從系統(tǒng)也提供了事務(wù)支持(即使不是強(qiáng)一致性)。然而,跨分片協(xié)調(diào)交易的難度是如此的瑣碎和復(fù)雜,許多分片系統(tǒng)是決定徹底放棄它們的。
什么是主主可用性?
主主可用性意味著數(shù)據(jù)庫至少有兩個主節(jié)點(diǎn),它們對數(shù)據(jù)進(jìn)行分片并執(zhí)行對數(shù)據(jù)庫的寫入。主主可用性代表了從主從的演變,通過讓集群中的節(jié)點(diǎn)提供讀寫服務(wù),使數(shù)據(jù)庫能夠擴(kuò)展到單臺機(jī)器之外。
考慮到分片數(shù)據(jù)庫難以管理且功能不全,工程師們開始開發(fā)至少可以解決其中一個問題的系統(tǒng)。這時候出現(xiàn)的是仍然不支持事務(wù)的系統(tǒng),但管理起來已經(jīng)非常容易。隨著對應(yīng)用程序正常運(yùn)行時間的需求不斷增加,幫助團(tuán)隊(duì)滿足其 SLA 的決定是很明智的。
這些系統(tǒng)背后的動機(jī)是每個實(shí)例節(jié)點(diǎn)都可以包含集群的部分(或全部)數(shù)據(jù),并為其提供讀取和寫入服務(wù)。每當(dāng)一個節(jié)點(diǎn)收到寫入請求時,它都會將更改傳播到所有其他需要它的副本的節(jié)點(diǎn)。為了處理兩個節(jié)點(diǎn)對同一個鍵值寫入的情況,任何一個節(jié)點(diǎn)的轉(zhuǎn)換在提交之前都會被送入沖突解決算法。鑒于每個站點(diǎn)都是“活躍的”,因此被稱為主主。
因?yàn)槊颗_服務(wù)器都可以對其所有數(shù)據(jù)進(jìn)行讀寫,所以分片更容易在算法上實(shí)現(xiàn),并使部署更易于管理。
在可用性方面,主主非常出色。如果一個節(jié)點(diǎn)發(fā)生故障,客戶端只需重定向到另一個確實(shí)包含數(shù)據(jù)的節(jié)點(diǎn)。只要數(shù)據(jù)的單個副本處于活動狀態(tài),就可以為其提供讀取和寫入服務(wù)。
雖然這種方案在高可用性方面非常出色,但其設(shè)計(jì)在一致性和數(shù)據(jù)正確性方面存在根本性的問題。因?yàn)槊總€實(shí)例節(jié)點(diǎn)都可以處理鍵值的寫入(在故障轉(zhuǎn)移場景中也是如此),所以它在處理數(shù)據(jù)時保持?jǐn)?shù)據(jù)完全同步是非常困難的。該方案通常是通過沖突解決算法來調(diào)解實(shí)例之間的沖突,而該算法對如何“消除”不一致性的決策是粗粒度的。
由于該解決方案是事后完成的,是在客戶端已經(jīng)收到有關(guān)程序的響應(yīng)之后——并且理論上已經(jīng)根據(jù)該響應(yīng)執(zhí)行了其他業(yè)務(wù)邏輯——主主復(fù)制很容易在數(shù)據(jù)中生成異常。
然而,考慮到正常運(yùn)行時間的溢價(jià),停機(jī)成本被認(rèn)為大于潛在異常的成本,因此主主成為主要的復(fù)制類型。
大規(guī)模正確性
共識和多活可用性
主主似乎解決了基礎(chǔ)設(shè)施面臨的主要問題——提供高可用性。但它只是通過放棄事務(wù)來做到這一點(diǎn),這使得系統(tǒng)在強(qiáng)一致性需求的滿足上并不那么可信。
例如,谷歌在其廣告業(yè)務(wù)中使用了一個龐大而復(fù)雜的 MySQL 分片系統(tǒng),該系統(tǒng)嚴(yán)重依賴 SQL 的表達(dá)能力來任意查詢數(shù)據(jù)庫。因?yàn)檫@些查詢通常依賴二級索引來提高性能,所以它們必須與它們所派生的數(shù)據(jù)保持完全一致。
最終,這個系統(tǒng)變得足夠大,開始導(dǎo)致分片 MySQL 出現(xiàn)問題,工程師開始設(shè)想如何解決這樣的問題:既要有大規(guī)模可伸縮的系統(tǒng),又要提供業(yè)務(wù)所需的強(qiáng)一致性。主主缺乏事務(wù)支持意味著它不應(yīng)該是一個可選項(xiàng),因此他們不得不設(shè)計(jì)一些新東西。最終,他們用這樣的一個系統(tǒng)解決了問題,這是一個基于共識復(fù)制的系統(tǒng),既能保證一致性,又能提供高可用性。
使用共識復(fù)制,寫入被提議到一個節(jié)點(diǎn),然后被復(fù)制到一些其他節(jié)點(diǎn)。一旦大多數(shù)節(jié)點(diǎn)確認(rèn)寫入,就可以提交。
共識和高可用性
這里的關(guān)鍵概念是,共識復(fù)制是介于同步和異步復(fù)制之間的一種機(jī)制:您可以指定任意數(shù)量的節(jié)點(diǎn)來進(jìn)行同步,但這些節(jié)點(diǎn)是哪些并不重要。這意味著集群可以容忍少數(shù)節(jié)點(diǎn)宕機(jī),而不會影響系統(tǒng)的可用性。(處理被關(guān)機(jī)服務(wù)器流量等的注意事項(xiàng))
然而,共識的代價(jià)是它需要節(jié)點(diǎn)與其他節(jié)點(diǎn)進(jìn)行通信以執(zhí)行寫入。雖然您可以采取一些措施來減少節(jié)點(diǎn)之間產(chǎn)生的延遲,例如將它們放在同一個可用區(qū)中,但這需要和高可用性一起權(quán)衡考慮。
例如,如果所有節(jié)點(diǎn)都在同一個數(shù)據(jù)中心,它們之間的通信速度很快,但如果整個數(shù)據(jù)中心離線,你的服務(wù)也不會獨(dú)活。將您的節(jié)點(diǎn)分散到多個數(shù)據(jù)中心可能會增加寫入所需的延遲,但卻可以提高你的可用性,就算整個數(shù)據(jù)中心都離線了,你的應(yīng)用也仍然在線。
什么是多主可用性?
多主可用性要求數(shù)據(jù)庫至少具有三個活動節(jié)點(diǎn),每個活動節(jié)點(diǎn)都可以對集群中的任何數(shù)據(jù)進(jìn)行讀寫而不產(chǎn)生沖突。
CockroachDB 實(shí)現(xiàn)了Google Spanner 論文中的大部分內(nèi)容(但值得注意的是,它不需要原子鐘),包括那些超越共識復(fù)制之外的特性,這些特性使可用性變得更簡單。為了描述其工作原理并將其與主主區(qū)分開來,我們創(chuàng)造了術(shù)語多主可用性。
主主 vs. 多主
主主通過允許集群中的任何節(jié)點(diǎn)為其鍵值提供讀寫服務(wù)來實(shí)現(xiàn)可用性,但只有在提交寫之后才將其接受的更改傳播給其他節(jié)點(diǎn)。
另一方面,多主可用性允許任何節(jié)點(diǎn)提供讀寫服務(wù),但確保大多數(shù)副本在寫入時保持同步,并且僅提供來自最新版本副本的讀取服務(wù)。
在高可用性方面,主主只需要一個副本即可同時用于讀寫,而多主則需要大多數(shù)副本在線才能達(dá)成共識(這仍然允許系統(tǒng)內(nèi)部出現(xiàn)部分故障)。
顯然這些數(shù)據(jù)庫在可用性方面的不同表現(xiàn)源于系統(tǒng)在對一致性方面處理的差異。主主數(shù)據(jù)庫在大多數(shù)情況下都會努力工作寫入數(shù)據(jù),但是不能保證客戶端現(xiàn)在或?qū)砟軌蜃x取到該數(shù)據(jù)。而多主數(shù)據(jù)庫僅在可以保證以后可以以一致的方式讀取數(shù)據(jù)時才接受寫入。
回顧與展望
在過去的 30 年中,數(shù)據(jù)庫復(fù)制和可用性取得了長足的進(jìn)步,現(xiàn)在已經(jīng)支持全球范圍內(nèi)部署,感覺就像它們永遠(yuǎn)不會不受歡迎。該領(lǐng)域的首次嘗試通過主從復(fù)制奠定了重要的基礎(chǔ),但最終,我們需要更好的可用性和更大的規(guī)模。
在這個領(lǐng)域,業(yè)界發(fā)展出了兩種主要的數(shù)據(jù)庫類型:其中主從用于滿足那些主要關(guān)注快速寫入的應(yīng)用程序,而多主則服務(wù)于那些對一致性有需要的應(yīng)用程序。
我們都期待有一天,我們可以利用量子糾纏并轉(zhuǎn)向下一代數(shù)據(jù)庫類型:可管理的分布式數(shù)據(jù)庫。
譯者介紹
崔瑩峰,51CTO社區(qū)編輯,一名70后程序員,擁有10多年工作經(jīng)驗(yàn),長期從事 Java 開發(fā),架構(gòu)設(shè)計(jì),容器化等相關(guān)工作。精通Java,熟練使用Maven、Jenkins等Devops相關(guān)工具鏈,擅長容器化方案規(guī)劃、設(shè)計(jì)和落地。
原文鏈接:??https://www.cockroachlabs.com/blog/brief-history-high-availability/??