《數(shù)據(jù)密集型應(yīng)用系統(tǒng)設(shè)計》章節(jié)總結(jié) 第五章 數(shù)據(jù)復(fù)制

目的:低延遲、高可用、高容量

主要方式:主從復(fù)制、多主節(jié)點(diǎn)復(fù)制、無主節(jié)點(diǎn)復(fù)制

復(fù)制策略:同步復(fù)制、異步復(fù)制

主節(jié)點(diǎn)與從節(jié)點(diǎn)

主從復(fù)制工作原理:

  1. 選定某個副本為主節(jié)點(diǎn),寫入工作通過主節(jié)點(diǎn)進(jìn)行
  2. 主節(jié)點(diǎn)寫入數(shù)據(jù)后,將數(shù)據(jù)的更改發(fā)送給從節(jié)點(diǎn)
  3. 讀取數(shù)據(jù)可以從主節(jié)點(diǎn)活從節(jié)點(diǎn)進(jìn)行

同步復(fù)制與異步復(fù)制

全同步:主節(jié)點(diǎn)復(fù)制需要等待全部從節(jié)點(diǎn)完成并反饋

半同步:主節(jié)點(diǎn)復(fù)制只需等待一個從節(jié)點(diǎn)完成并反饋

全異步:主節(jié)點(diǎn)發(fā)送復(fù)制消息后,無需等待從節(jié)點(diǎn)復(fù)制完成的反饋

配置新的從節(jié)點(diǎn)

建立新的從節(jié)點(diǎn)的過程:

  1. 生成快照
  2. 基于快照的全量復(fù)制
  3. 從節(jié)點(diǎn)連接主節(jié)點(diǎn)獲取快照后所有修改的日志
  4. 根據(jù)日志進(jìn)行增量復(fù)制

處理節(jié)點(diǎn)失效

主節(jié)點(diǎn)和從節(jié)點(diǎn)都存在失效的可能性,因此應(yīng)當(dāng)采取手段盡量保持系統(tǒng)的正常運(yùn)行。

從節(jié)點(diǎn)失效:追趕式恢復(fù)

從節(jié)點(diǎn)失效后,重新獲取在失效后主節(jié)點(diǎn)更新的日志,依據(jù)日志進(jìn)行恢復(fù)

主節(jié)點(diǎn)失效:節(jié)點(diǎn)切換

需要選取某個從節(jié)點(diǎn)為主節(jié)點(diǎn),通常具有三個步驟:

  1. 確定主節(jié)點(diǎn)失效:通常通過心跳包的響應(yīng)時間判斷
  2. 選舉主節(jié)點(diǎn):指定一個所有節(jié)點(diǎn)統(tǒng)一的主節(jié)點(diǎn),該節(jié)點(diǎn)最好與主節(jié)點(diǎn)數(shù)據(jù)差異較小
  3. 重新配置主節(jié)點(diǎn):需要令客戶端將請求發(fā)送至新節(jié)點(diǎn)

但是主節(jié)點(diǎn)的切換存在多種可能的問題:

  • 異步數(shù)據(jù)丟失
  • 腦裂
  • 心跳響應(yīng)時間閾值設(shè)定

復(fù)制日志的實(shí)現(xiàn)

基于語句的復(fù)制

主從復(fù)制過程中直接復(fù)制主節(jié)點(diǎn)執(zhí)行的邏輯語句,但是這存在一些問題,比如在語句中應(yīng)用了時間獲取、隨機(jī)數(shù)生成等操作。

MySQL5.1之前的版本采用基于語句的復(fù)制方式。

基于預(yù)寫日志(WAL)傳輸

對于負(fù)責(zé)向磁盤寫入內(nèi)容的存儲引擎,都會在寫入磁盤之前向WAL中追加將要寫入的字節(jié)序列,WAL可以理解為一種物理日志,用來記錄對于磁盤的修改,但是仍然有一些問題,WAL與存儲引擎強(qiáng)相關(guān),該方式可能無法適應(yīng)存儲格式的改變,不能兼容運(yùn)行于主從節(jié)點(diǎn)不同的引擎。

Oracle等系統(tǒng)支持該方式。

基于行的邏輯日志復(fù)制

邏輯日志是用來記錄行的增刪改行為的記錄,例如MySQL中的二進(jìn)制日志binlog,邏輯日志與存儲引擎解耦,可以通過復(fù)制邏輯日志實(shí)現(xiàn)主從復(fù)制。

MySQL當(dāng)今應(yīng)用這種方式,還要配合relay log。

基于觸發(fā)器的復(fù)制

觸發(fā)器支持用戶注冊自己的應(yīng)用代碼,使得數(shù)據(jù)庫執(zhí)行數(shù)據(jù)更改時自動執(zhí)行該代碼。通過該技術(shù)可以將數(shù)據(jù)更改記錄同步到另一個表,外部處理邏輯訪問該表從而實(shí)施必要的邏輯。

復(fù)制滯后問題

只要不采用全同步,就會產(chǎn)生各個副本數(shù)據(jù)不一樣的問題,也就是一致性問題

讀自己的寫

用戶A向數(shù)據(jù)庫執(zhí)行某更改操作后,后續(xù)再去讀取卻看不到剛剛的更改操作,這種情況違反了”讀寫一致性“。

可能由于從節(jié)點(diǎn)未及時復(fù)制主節(jié)點(diǎn),且讀操作由從節(jié)點(diǎn)執(zhí)行導(dǎo)致。

可能但不一定好使的解決方案:

  • 如果訪問可能被修改的數(shù)據(jù),從主節(jié)點(diǎn)讀取
  • 更新后一段時間內(nèi)保持從主節(jié)點(diǎn)讀取,但是延遲時間不一定好確定
  • 客戶端請求附帶時間戳,但是可能會有時鐘不可靠問題

另外還有跨設(shè)備的讀寫一致性問題

單調(diào)讀

用戶A對于數(shù)據(jù)庫先后執(zhí)行兩次讀取,后一次的數(shù)據(jù)比前一次的更舊,這種情況違反了”單調(diào)讀一致性“。

可能由于不同從節(jié)點(diǎn)復(fù)制完成時間不一致,且先訪問了已經(jīng)復(fù)制完成的節(jié)點(diǎn),后訪問未完成的節(jié)點(diǎn)導(dǎo)致。

可能的解決方案:同一用戶每次從同一個從節(jié)點(diǎn)讀取數(shù)據(jù)

前綴一致讀

用戶A觀察用戶B具有因果關(guān)系的兩次更改操作,但是卻觀察到”果“發(fā)生于“因”之前,這種情況違反了“前綴讀一致性”。

可能的解決方案:將具有因果關(guān)系的操作交由同一個從節(jié)點(diǎn)完成

復(fù)制滯后的解決方案

解決復(fù)制滯后屬于分布式事務(wù)的范疇,分布式事務(wù)對于性能可能造成極大的影響,這個問題放在后續(xù)章節(jié)詳細(xì)討論。

多主節(jié)點(diǎn)復(fù)制

主從復(fù)制將寫入操作放在同一個節(jié)點(diǎn),多主節(jié)點(diǎn)復(fù)制中有多個節(jié)點(diǎn)可以接受寫入操作,并將改寫入操作同步到其他主節(jié)點(diǎn),各個主節(jié)點(diǎn)之間保持?jǐn)?shù)據(jù)盡量同步。

使用場景

跨不同的數(shù)據(jù)中心進(jìn)行多主節(jié)點(diǎn)復(fù)制較為合理。

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

在部署于多個數(shù)據(jù)中心時,主從復(fù)制與多主節(jié)點(diǎn)復(fù)制的區(qū)別:

  • 性能:主從復(fù)制無法實(shí)現(xiàn)就近復(fù)制
  • 容忍數(shù)據(jù)中心失效:多主節(jié)點(diǎn)復(fù)制
  • 容忍網(wǎng)絡(luò)故障:多主節(jié)點(diǎn)采用異步復(fù)制,能夠更好的解決該問題

以上是多主節(jié)點(diǎn)復(fù)制的優(yōu)勢,然而具有一個極大的劣勢,不同的數(shù)據(jù)中心修改相同的數(shù)據(jù),即寫沖突

離線客戶端操作

如果用戶手機(jī)、電腦中的日歷等記錄需要和云服務(wù)器進(jìn)行同步,那么用戶設(shè)備和服務(wù)器可分別視為兩個主數(shù)據(jù)庫。

協(xié)作編輯

類似騰訊文檔,各個用戶都相當(dāng)于一個主節(jié)點(diǎn),但是需要用鎖解決并發(fā)沖突。

處理寫沖突

如何解決在兩個數(shù)據(jù)中心中對于相同數(shù)據(jù)執(zhí)行不同修改的問題呢?

同步與異步?jīng)_突檢測

同步?jīng)_突檢測:在對主節(jié)點(diǎn)A進(jìn)行修改時,同步等待其他節(jié)點(diǎn)修改完成,然而這樣相當(dāng)于回到了主從復(fù)制。

避免沖突

確保同一用戶的更改請求由固定的主節(jié)點(diǎn)執(zhí)行。不過也會產(chǎn)生一些問題,比如用戶坐飛機(jī)出差會導(dǎo)致對應(yīng)主節(jié)點(diǎn)的 切換。

收斂于一致狀態(tài)

如果各個主節(jié)點(diǎn)存在具有沖突的記錄,該保留那個,可能的方式有:

  • 根據(jù)時間相關(guān)的UUID
  • 根據(jù)各個副本的ID
  • 將結(jié)果組合
  • 設(shè)定預(yù)定的方案

自定義沖突解決邏輯

解決沖突更合適的方式是依靠其應(yīng)用者,此時有兩大類方式:

  • 寫入時執(zhí)行
  • 讀取時執(zhí)行

什么是沖突

事務(wù)的隔離性,幻讀等等問題,不必在此詳述。

拓?fù)浣Y(jié)構(gòu)

拓?fù)浣Y(jié)構(gòu):寫請求從一個節(jié)點(diǎn)路由到其他節(jié)點(diǎn)的路徑,典型結(jié)構(gòu)包括:環(huán)形,星形,全部-全部

無主節(jié)點(diǎn)復(fù)制

無主節(jié)點(diǎn)復(fù)制模式中,不存在主節(jié)點(diǎn)與從節(jié)點(diǎn)之分,所有節(jié)點(diǎn)都可以接受寫入請求,也就是全都是主節(jié)點(diǎn),該模式的兩種主要實(shí)現(xiàn)方式:

  • 分別發(fā)送多個副本
  • 協(xié)調(diào)者代為寫入

節(jié)點(diǎn)失效時寫入數(shù)據(jù)庫

可能存在部分節(jié)點(diǎn)失效,而客戶端只需獲得一定數(shù)量節(jié)點(diǎn)的寫入請求,無需全部節(jié)點(diǎn)的請求。

崩潰節(jié)點(diǎn)重新上線可能會使得系統(tǒng)中保存過期的數(shù)據(jù),此時不必立即同步,因?yàn)榭蛻舳俗x取數(shù)據(jù)時向所有節(jié)點(diǎn)發(fā)送請求,客戶端只需從得到的相應(yīng)中選取最新的就可以。

讀修復(fù)與反熵

崩潰節(jié)點(diǎn)重新上線后如何修復(fù)那些舊數(shù)據(jù)?Dynamo風(fēng)格數(shù)據(jù)庫兩種解決思路:

  • 讀修復(fù):在用戶讀取數(shù)據(jù)時發(fā)現(xiàn)某個版本數(shù)據(jù)版本過期時,客戶端將新值寫入存儲過期值的副本,懶漢式。
  • 反熵過程:后臺進(jìn)程定期尋找各個副本之間的差異,并進(jìn)行復(fù)制,餓漢式

讀寫quroum

由于節(jié)點(diǎn)可能出現(xiàn)故障,因此每次的寫入無法保證在所有節(jié)點(diǎn)都成功,那么應(yīng)該規(guī)定一個閾值,只要寫入成功的節(jié)點(diǎn)數(shù)大于等于該閾值就可以視為寫入成功。同樣的原因和道理,讀取操作也應(yīng)該規(guī)定這么一個閾值。

寫閾值w,讀閾值r,節(jié)點(diǎn)數(shù)目n,他們應(yīng)該滿足w+r>n,該方案稱為仲裁寫/讀。

Quorum一致性的局限

如果希望提高系統(tǒng)的可用性,那可以將w+r>n的條件放松至w+r>=n,此時可用性提高,但是更容易出現(xiàn)讀取數(shù)據(jù)的一致性問題。

即便是在w+r>n的條件下,也會存在一些問題:

  • 采用sloppy quorum使得寫入和讀取的節(jié)點(diǎn)可能完全不重復(fù)
  • 同時寫入的沖突問題
  • 寫操作異步導(dǎo)致讀取新值還是舊值不確定
  • 如果未能夠成功寫入超過w個節(jié)點(diǎn),但是不能回滾
  • 即便一切正常也會由于異步產(chǎn)生的單調(diào)讀問題

監(jiān)控舊值

對于主從復(fù)制的方式,可以很容易的通過日志判定從節(jié)點(diǎn)相對于主節(jié)點(diǎn)落后的程度。

對于無主節(jié)點(diǎn)復(fù)制系統(tǒng),由于每個節(jié)點(diǎn)都接收寫入,導(dǎo)致判定較為困難。

寬松的quorum與數(shù)據(jù)回傳

如果客戶端寫入/讀取能夠連接到的節(jié)點(diǎn)無法滿足w/r的需求,那么有以下方式應(yīng)對:

  • 如果無法滿足要求,是否要將錯誤明確返回
  • 接受這些請求,并將他們暫時存儲在一些可以訪問的其他節(jié)點(diǎn),這些節(jié)點(diǎn)不屬于原有的n個節(jié)點(diǎn),該方法稱為放松的仲裁(sloppy quorum)

sloppy能很好提升系統(tǒng)的可用性。

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

如何令響應(yīng)時間更小

檢測并發(fā)寫

對于多主節(jié)點(diǎn)復(fù)制,即便采用嚴(yán)格的quorum機(jī)制,由于異步的復(fù)制操作,也會引起一致性問題,因此需要一些解決該問題的方案。

最后寫入者獲勝(丟棄并發(fā)寫)

最后寫入者獲勝(Last Write Win),通過時間戳確定,但是該方法會犧牲數(shù)據(jù)的持久性能。

happens-before關(guān)系和并發(fā)

并發(fā):在先后順序上存在依賴關(guān)系的兩個步驟存在并發(fā)

并發(fā)的關(guān)鍵不在于時間上的同時,而在于邏輯上的依賴,在于兩個操作是否需要意識到對方的存在。

確定前后關(guān)系

介紹了一種確定操作并發(fā)性的方法。

合并同時寫入的值

如何將并發(fā)操作合并,涉及到了如何進(jìn)行刪除操作

版本矢量

版本矢量:各個節(jié)點(diǎn)版本號的集合

小結(jié)

  • 復(fù)制的目的
  • 復(fù)制的方案
    • 主從復(fù)制
    • 多主節(jié)點(diǎn)復(fù)制
    • 無主節(jié)點(diǎn)復(fù)制
  • 一致性問題
    • 讀寫一致性
    • 單調(diào)讀一致性
    • 前綴讀一致性
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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