第五章備份-part3,designing Data-Intensive Applications 中文翻譯摘要

多Leader備份(Multi-Leader Replication)

這章當(dāng)目前位置我們都在討論單Leader的備份架構(gòu),這個方案用的很普遍,但也有些問題。因為只有一個Leader,所有寫請求都要通過通過這個Leader,一旦客戶端和Leader連接斷了,就不能寫了。

一個最自然的解決方法是多個節(jié)點都可以接受寫請求,備份方式是一樣的,每個節(jié)點處理一個寫請求后把更新發(fā)送給其他節(jié)點。我們管他叫多Leader配置。(multi-leader configuration)。每個節(jié)點都是Leader,有都是其他Leader的Follower。

多Leader備份的應(yīng)用場景

如果你只有單個數(shù)據(jù)中心,那就不要用了,因為收益遠(yuǎn)比付出的代價小。但是在某些場景下這么搞是有意義的。

多數(shù)據(jù)中心

如果你的服務(wù)是多地部署的,那Leader只能在某一地的機(jī)房,這樣所有的寫請求都要發(fā)到這個機(jī)房去??鐧C(jī)房跨地域的請求會很慢。在多Leader的配置下,你可以每個機(jī)房設(shè)置一個Leader,在機(jī)房內(nèi)部,還是運(yùn)用前面講到的Leader和Follower的機(jī)制做備份。機(jī)房之間,2個Leader之間彼此把自己的更新發(fā)送給對方。方法如Figure 5-6


那么我們來對比下在多數(shù)據(jù)中心場景下,單Leader和多Leader的優(yōu)劣

  • 性能
    在單Leader下,每個寫請求都必須翻山越嶺發(fā)到某一個數(shù)據(jù)中心的Leader去,這個會有很大的耗時,而且這違背了最初設(shè)立多個數(shù)據(jù)中心的目的。在多Leader下,每次寫請求可以發(fā)送給本地的Leader,然后在異步的同步給其他的數(shù)據(jù)中心。從用戶側(cè)看來,性能更好。
  • 數(shù)據(jù)中心容錯性
    單Leader配置下,一旦Leader所在的數(shù)據(jù)中心掛了,失敗重啟(failover)機(jī)制會在另一個數(shù)據(jù)中心再選一個Leader。在多Leader場景下,其他的數(shù)據(jù)中心可以繼續(xù)工作,等到異常的數(shù)據(jù)中心恢復(fù)后,再讓這個中心備份重新同步
  • 網(wǎng)絡(luò)異常容錯性
    跨數(shù)據(jù)中心的網(wǎng)絡(luò)往往走公網(wǎng),這就要比內(nèi)部網(wǎng)絡(luò)可靠性差很多了。單Leader 情況下,網(wǎng)絡(luò)問題十分重要,因為一旦網(wǎng)絡(luò)不好,這個系統(tǒng)就跪了。但是多Leader情況下對網(wǎng)絡(luò)的容錯性就好很多。因為客戶端可以先發(fā)個本地的Leader然后異步的在Leader之間同步。

有些數(shù)據(jù)庫默認(rèn)支持多Leader,但是往往都是外部實現(xiàn)的。例如MySQL的Tungsten Replicator, PostgreSQL 的BDR,Oracle的GoldenGate。

雖說多Leader的配置很多數(shù)據(jù)庫都作為一個額外特征,但是他和很多其他的數(shù)據(jù)庫特征在一起可能會有很奇怪的問題。比如自增鍵, 觸發(fā)器,完整性約束可能都是個問題。所以一般來說多Leader很多時候都是個危險的東西,能不用就不用。

離線操作

如果你的應(yīng)用需要即使斷網(wǎng)也能正常工作,那多Leader就很好用了。比如你有一個日歷的app, 無論身處何地,網(wǎng)絡(luò)如何,都要能夠正常查看你的會議(讀請求),加入新的會議(寫請求)。這就要求當(dāng)你做任何更新的時候,在設(shè)備下次連上網(wǎng)絡(luò)的時候,需要把這些更新發(fā)送給服務(wù)器和其他的設(shè)備。

這種情況下,每個設(shè)備有一個自己的本地數(shù)據(jù)庫作為Leader,同時還有一個異步的多Leader同步流程把你的數(shù)據(jù)正確備份到所有設(shè)備去。備份節(jié)點的落后可能有幾個小時甚至幾天。

從架構(gòu)角度講,這跟多個數(shù)據(jù)中心間進(jìn)行多Leader備份沒什么區(qū)別。每個設(shè)備是一個數(shù)據(jù)中心,網(wǎng)絡(luò)連接非常不可靠。在日歷同步的豐富歷史中,多Leader備份機(jī)制是表現(xiàn)不錯的一個。

協(xié)同編輯

實時協(xié)同編輯應(yīng)用允許多個人同時編輯一個文檔。比如Etherpad和Google Docs。我們一般不把這件事情當(dāng)做一個數(shù)據(jù)庫同步的問題,但是當(dāng)出現(xiàn)離線編輯的時候,就跟上面的問題比較像了。當(dāng)一個用戶修改文檔時,首先把更新發(fā)送給本地的備份,然后異步的把更新發(fā)送給server和其他用戶。

如果你要保證永遠(yuǎn)沒有編輯沖突,那應(yīng)用就需要在一個用戶編輯前加鎖。當(dāng)一個用戶想要修改文檔時,他必須等前一個人提交他們的修改并且釋放鎖。這種協(xié)同模型就跟單Leader備份加事務(wù)的方式很像了。但是為了更快,你可能希望每次更新更新的內(nèi)容很小也不要鎖。但這就會帶來多Leader備份的各種問題,比如沖突解決。

應(yīng)對寫入沖突

多Leader架構(gòu)最大的問題就是寫入沖突,所以我們需要一個重提解決機(jī)制。舉個例子,假設(shè)一個網(wǎng)頁被兩個人同時編輯,用戶1把標(biāo)題從A改成B,用戶2把標(biāo)題從A改成C,兩個用戶的成功把自己的更新提交到了本地的Leader。但是當(dāng)更新異步的在節(jié)點中同步時,這就有沖突了。這個問題在單Leader的數(shù)據(jù)庫中是不存在的。


同步方案 vs 異步?jīng)_突檢測

在單Leader時,在第一個提成功提交之前,第二個請求會一直阻塞住,或者直接失敗了。但是在多Leader時,兩個寫入都會成功,這種沖突會在未來的某個時間點異步備份時被發(fā)現(xiàn)。這個時候讓用戶來解決沖突已經(jīng)太晚了。從道理上來說,你可以在線的檢測沖突,也就是說只有在成功把更新數(shù)據(jù)發(fā)送到所有其他的節(jié)點后才告訴用戶更新成功了。但是這就是去了多Leader的優(yōu)勢,你沒有辦法再允許每個備份節(jié)點獨(dú)立的接受寫請求了。如果要同步的沖突檢測,還不如就搞一個單Leader備份框架呢。

避免沖突

最簡單的處理沖突的方法就是避免沖突。如果應(yīng)用能夠保證對一條數(shù)據(jù)的所有寫都發(fā)給同一個Leader,就不會有沖突了。因為大部分多Leader備份的實現(xiàn)對于沖突的處理能力都很弱,避免沖突往往是一個推薦的方法。

舉個例子,如果一個用戶修改他自己的數(shù)據(jù),那你可以將特定用戶的請求永遠(yuǎn)發(fā)給相同的數(shù)據(jù)中心,用這個數(shù)據(jù)中心的Leader進(jìn)行讀寫。不同的用戶有不同的數(shù)據(jù)中心(比如基于他們的地理位置選最近的數(shù)據(jù)中心),從單個用戶的角度來看,這就變成了一個單Leader的架構(gòu)。

當(dāng)時有時候你可能會需要修改用戶對應(yīng)的數(shù)據(jù)中心,可能因為某一個數(shù)據(jù)中掛了,也可能是因為用戶地理位置變了,你要把他移到當(dāng)前離他最近的數(shù)據(jù)中心。這種情況下,又會有沖突出來,因為在某段時間內(nèi),同一個用戶會寫兩個Leader。

一致性收斂

單Leader的架構(gòu)下,寫請求是有順序的,多個更新同時發(fā)生時,最后一個寫確定了這個字段的最終值。但是多Leader下,寫入就沒有一個確定順序了。在Figure 5-7中,leader 1的標(biāo)題先改成B,后又變成C,Leader 2卻是先是C,后是B。這兩個順序沒有誰比誰更對的問題。

如果每個備份節(jié)點只根據(jù)他收到請求的順序更新自己的數(shù)據(jù),那最終就會使一個不一致狀態(tài)。leader 1的最終結(jié)果是C, leader 2的最終結(jié)果是B,這是不能接受的。我們要求所有備份最終的結(jié)果必須是一樣的,這也就要求數(shù)據(jù)庫必須用一種收斂的方法來處理沖突。這就有很多方法了

  • 給每個寫一個唯一id(時間戳,隨機(jī)數(shù),UUID,key/value的hash值),選id最大的寫請求作為贏家,把其他的都丟掉。如果用時間戳作為id,就加作最后寫生效last write wins, LWW).LWW 我們這章最后還會講。
  • 給每個備份節(jié)點一個唯一id,沖突時優(yōu)先選用id大的節(jié)點的數(shù)據(jù),但這會造成數(shù)據(jù)丟失。
  • 合并值, 比如把他們按照字母序合并,F(xiàn)igure 5-7的結(jié)果就會變成B/C
  • 把沖突記成一種特殊的數(shù)據(jù)格式然后交由應(yīng)用后期處理。

自定義沖突解決邏輯

其實最合適解決沖突的方法依賴于應(yīng)用,多Leader備份架構(gòu)很多都允許你自定義沖突解決代碼。這段代碼可能是在寫入或者讀取時執(zhí)行。

  • 寫入執(zhí)行
    當(dāng)數(shù)據(jù)庫系統(tǒng)在備份日志中檢測到?jīng)_突,他會調(diào)用沖突處理器。這種處理器一般無法提示用戶,因為他是在后臺進(jìn)程中執(zhí)行的,所以必須速度很快。Bucardo就是這么搞的。
  • 讀取執(zhí)行
    當(dāng)發(fā)現(xiàn)沖突時,所有沖突的現(xiàn)場都被保留下來。當(dāng)這條數(shù)據(jù)下次被讀取的時候,這條數(shù)據(jù)的多個版本都會返回給應(yīng)用。應(yīng)用把這個沖突數(shù)據(jù)提示給用戶,然后由用戶手動解決沖突,再把最終結(jié)果寫到數(shù)據(jù)庫中。CouchDB 就是這么搞的。

有一點注意,沖突解決往往是針對一條數(shù)據(jù),而不是一個事務(wù)。所以雖然你的一個事務(wù)可能一次性包含多個寫請求,但是他們還是在沖突解決中分開處理的,也就說可能會不一致。

什么是沖突?

有時候沖突其實很顯然,就好像Figure 5-7一樣,兩個人同時修改同一條數(shù)據(jù)的同一個字段,毫無疑問這是沖突。

但是其他的沖突就很難檢測了。比如想象你有一個會議室,這個會議室在任何時刻都只能有一個人預(yù)訂。在這種場景下,當(dāng)一個會議室在某個時間點有超過1個人預(yù)訂時,這就是一個沖突了。(但是他和Figure 5-7的區(qū)別是他不再是同一個字段的問題,而是一個時間段的問題,就好像2個人一個人預(yù)約7-8,一個人預(yù)約9-10就不沖突,但是一個人預(yù)約7-10,一個人預(yù)約9-11就沖突了。)即使應(yīng)用層在預(yù)訂之前檢查會議室在這個時間段是否可用,也不一定能避免沖突,因為請求可能發(fā)給多個Leader導(dǎo)致沖突。

多Leader備份拓?fù)鋱D

備份拓?fù)洌?em>replication topology)是指更新在節(jié)點間傳播的路徑,如果你像Figure 5-7一樣只有2個節(jié)點,那就很簡單了。但是如果你有多個節(jié)點,就可能有多鐘拓?fù)潢P(guān)系,例如Figure 5-8

最常見的拓?fù)潢P(guān)系是全相連,如(c)。但是其他的拓?fù)浣Y(jié)構(gòu)也有人用,比如MySQL默認(rèn)是有環(huán)裝拓?fù)?,?a), 每個節(jié)點從上個節(jié)點接收更新請求,把收到的更新加上自己的更新發(fā)給下一個節(jié)點。(b)中的星型結(jié)構(gòu)也很流行,一個節(jié)點負(fù)責(zé)總控分發(fā),其他節(jié)點只跟總控通信。

在環(huán)型和星型拓?fù)渲小R粋€寫請求要傳遞多個節(jié)點才能實現(xiàn)所有節(jié)點都備份完成。因此一個節(jié)點要把他收到的內(nèi)容進(jìn)行轉(zhuǎn)發(fā)。為了防止無限循環(huán),每個節(jié)點都會有一個唯一的編號,當(dāng)一個節(jié)點處理完一條備份日志后,會給這個日志當(dāng)上這個節(jié)點的id。當(dāng)再次收到這條日志發(fā)現(xiàn)已經(jīng)打上了自己的id后,節(jié)點直接把這條日志丟掉。

環(huán)型和星型的問題是一旦有一個節(jié)點掛了,那整個系統(tǒng)的消息備份都會受影響。這就要求系統(tǒng)必須在一個節(jié)點掛掉后重新配置通信的節(jié)點,去掉失效節(jié)點,但是這個工作往往依賴于人工介入。這點就是全連接的拓?fù)浣Y(jié)構(gòu)的優(yōu)勢所在,他有著更好的容錯性,因為一個節(jié)點掛了,其他節(jié)點還是能從別的通路中獲取到所有的更新消息。

不要以為全連接就沒有問題,在網(wǎng)絡(luò)環(huán)境中,有些鏈路會比其他的更快,這可能會導(dǎo)致有些更新被莫名其妙的覆蓋了。就像Figure 5-9一樣??蛻舳薃 通過Leader1插入了一條數(shù)據(jù), 客戶端B通過Leader3更新這條數(shù)據(jù),但是Leader 2收到的日志順序就跟實際不一樣了,這就是前面講到的邏輯前后順序的問題。這種情況下,簡單的用時間戳都不能解決問題,因為時間戳也不一定能保證時序的嚴(yán)格一直,這個第8章講。


為了解決這個問題,用到了一個叫版本列表(version vectors)的技術(shù),這個也后面再講了。但是殘酷的事實是沖突解決在很多多Leader備份的系統(tǒng)中實現(xiàn)的都很差。如果你要用一個而類似這樣的系統(tǒng),一定是小心這些問題,仔細(xì)讀文檔。充分測試,確保他的實際工作結(jié)果跟你預(yù)想的一樣。

?著作權(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)容