DDIA笔记——数据复制
Table of Contents generated with DocToc
此篇為《數(shù)據(jù)密集型應(yīng)用系統(tǒng)設(shè)計》(DDIA)讀書筆記,筆記可能存在遺漏,建議直接閱讀原書。
第五章 數(shù)據(jù)復(fù)制
- 主從復(fù)制
- 復(fù)制滯后
- 復(fù)制滯后帶來的問題
- 多主節(jié)點復(fù)制
- 適用場景
- 處理寫沖突
- 拓?fù)浣Y(jié)構(gòu)
- 無主節(jié)點復(fù)制
- 讀修復(fù)和反熵
- 讀寫quorum
- 寬松的quorum和數(shù)據(jù)回傳
- 多數(shù)據(jù)中心操作
- 檢測并發(fā)寫
- 合并同時寫入的值
- 版本矢量
第五章 數(shù)據(jù)復(fù)制
主從復(fù)制
復(fù)制滯后
如果一個應(yīng)用正好從一個異步的從節(jié)點讀取數(shù)據(jù),而該副本落后于主節(jié)點,則應(yīng)用可能會讀到過期的信息。由于并非所有的寫入都反應(yīng)到從副本上,如果同時對主節(jié)點和從節(jié)點發(fā)起相同的查詢,可能會得到不同的結(jié)果。但如果停止寫數(shù)據(jù)庫,經(jīng)過一段時間后,從節(jié)點最終會追上主節(jié)點并與主節(jié)點保持一致,這種效應(yīng)也被稱為最終一致性。
復(fù)制滯后帶來的問題
三種復(fù)制滯后帶來的問題場景:
1.讀自己的寫
許多應(yīng)用會讓用戶提交一些數(shù)據(jù),然后查看自己所提交的內(nèi)容,對于異步復(fù)制會出現(xiàn)這樣的問題:提交數(shù)據(jù)須發(fā)送到主節(jié)點,但是當(dāng)用戶讀取數(shù)據(jù)時,數(shù)據(jù)可能來自從節(jié)點,然而這時新數(shù)據(jù)可能尚未到達(dá)從節(jié)點,導(dǎo)致用戶以為提交的數(shù)據(jù)丟失。
針對這種情況,我們需要“讀寫一致性”,該機制保證如果用戶重新加載頁面,他們總能看到自己最近提交的更新,但對其他用戶則沒有任何保證(其他用戶的更新可能無法及時看到)。
基于主從復(fù)制的系統(tǒng)實現(xiàn)讀寫一致性的可行方案:
- 如果用戶訪問可能會被修改的內(nèi)容,從主節(jié)點讀取;否則,在從節(jié)點讀取;所以在查詢執(zhí)行之前需要判斷內(nèi)容是否可能被修改;
- 如果應(yīng)用的大部分內(nèi)容都必須經(jīng)由主節(jié)點,這就喪失了讀操作的擴(kuò)展性。這就需要其他的方案,例如:跟蹤最近的更新時間,如果更新后一分鐘之內(nèi),則從主節(jié)點讀取;同時監(jiān)控從節(jié)點的復(fù)制滯后程度,避免從那些滯后時間過長的從節(jié)點讀取;
- 讀請求+客戶端記住最近更新時的時間戳,系統(tǒng)確保對該用戶提供讀服務(wù)時都應(yīng)該至少包含了該時間戳的更新,否則可以交給另一個副本來處理或等待至副本接收到最近的更新;
- 如果副本分布在多數(shù)據(jù)中心,必須先把請求路由到主節(jié)點所在的數(shù)據(jù)中心(?);
如果同一用戶從多個設(shè)備訪問數(shù)據(jù)時,還有一些需要考慮的問題:
- 記住用戶上次更新時間戳的方法實現(xiàn)起來會比較困難;
- 如果副本分布在多數(shù)據(jù)中心,無法保證來自不同設(shè)備的連接經(jīng)過路由之后都到達(dá)同一數(shù)據(jù)中心;
2.單調(diào)讀
如圖,用戶1234發(fā)出一條寫請求,主節(jié)點執(zhí)行完畢之后,將請求轉(zhuǎn)發(fā)給其他從節(jié)點,其中從節(jié)點1復(fù)制滯后較短,從節(jié)點2復(fù)制滯后較長。此時,用戶2345發(fā)起了兩次對reply_to=5555的讀請求,第一次查詢成功得到了用戶數(shù)據(jù),而第二次請求,由于滯后過長,導(dǎo)致返回為空。
單調(diào)讀一致性保證:當(dāng)讀取數(shù)據(jù)時,如果某個用戶依次進(jìn)行多次讀取,則他不會看到回滾現(xiàn)象,即在讀取較新值之后又發(fā)生讀舊值的情況。
實現(xiàn)單調(diào)讀的一種方式是:確保每個用戶總是從固定的同一副本執(zhí)行讀取(例如:基于用戶ID的哈希算法來選擇從節(jié)點);
3.前綴一致讀
復(fù)制滯后帶來的因果反常:
Mr. Pooms: How far into the future can you see, Mrs. Cake?
Mrs. Cake: About ten seconds usually, Mr. Pooms.
但是由于滯后程度不同其他人聽到的可能是:
Mrs. Cake: About ten seconds usually, Mr. Pooms.Mr. Pooms: How far into the future can you see, Mrs. Cake?
為了防止這種異常,引入了另一種保證:前綴一致讀(對于一系列按照某個順序發(fā)生的寫請求,那么讀取這些內(nèi)容是也會按照當(dāng)時寫入的順序)。
一種解決方案是確保任何具有因果順序關(guān)系的寫入都交給一個分區(qū)來完成,但該方案的真實效率會大打折扣。
多主節(jié)點復(fù)制
由主從復(fù)制模型中“一主多從”變?yōu)椤岸嘀鞫鄰摹?#xff0c;復(fù)制流程類似:處理寫的每個主節(jié)點都必須將該數(shù)據(jù)更改轉(zhuǎn)發(fā)到所有其他節(jié)點 。此時,每個主節(jié)點還是其他主節(jié)點的從節(jié)點。
適用場景
1.多數(shù)據(jù)中心
數(shù)據(jù)中心內(nèi)部采用主從復(fù)制方案,而在數(shù)據(jù)中心之間,由各個數(shù)據(jù)中心的主節(jié)點來負(fù)責(zé)同其他數(shù)據(jù)中心的主節(jié)點進(jìn)行數(shù)據(jù)的交換和更新。
每個數(shù)據(jù)中心之間通過廣域網(wǎng)連接。
缺點:不同的數(shù)據(jù)中心可能會同時修改相同的數(shù)據(jù),因而必須解決潛在的寫沖突。
2.離線客戶端操作——網(wǎng)絡(luò)斷開后還要繼續(xù)工作
3.協(xié)作編輯(通常不會將協(xié)作編輯等價于數(shù)據(jù)庫復(fù)制問題,但二者有很多相似之處)
處理寫沖突
1.避免沖突
確保特定用戶的更新請求總是路由到特定的數(shù)據(jù)中心,并在該數(shù)據(jù)中心的主節(jié)點上進(jìn)行讀/寫。從用戶角度來看,這基本就等價于主從復(fù)制模型來。。。
2.收斂于一致狀態(tài)
至少要保證數(shù)據(jù)在所有副本中最終狀態(tài)是一致的。
- 為每個寫入分配一個特定的ID,并制定規(guī)則,挑選勝利者;
- 將所有寫入合并為一條,在應(yīng)用層進(jìn)行處理;
3.解決沖突最合適的方式可能還是依靠應(yīng)用層
例如:保存導(dǎo)致沖突的所有可能,在讀取數(shù)據(jù)時,由應(yīng)用層處理。
拓?fù)浣Y(jié)構(gòu)
環(huán)形和星形拓?fù)浯嬖诘膯栴}:如果某一節(jié)點發(fā)生故障,在修復(fù)之前,會影響其他節(jié)點之間復(fù)制日志的轉(zhuǎn)發(fā);
全鏈接拓?fù)浯嬖诘膯栴}:存在某些網(wǎng)絡(luò)鏈路比其他鏈路更快的情況,從而導(dǎo)致復(fù)制日志之間的覆蓋;
沖突檢測技術(shù)在許多多主節(jié)點復(fù)制系統(tǒng)中的實現(xiàn)還不夠完善
無主節(jié)點復(fù)制
同時向所有副本發(fā)出寫請求/讀請求。
讀修復(fù)和反熵
當(dāng)一個失效節(jié)點重新上線后,如何趕上中間錯過的那些寫請求?兩種機制:
- 讀修復(fù)
當(dāng)客戶端并行讀取多個副本時,可以檢測到過期值(根據(jù)版本號),然后將新值寫入到該副本;這種方法主要適合那些被頻繁讀取到場景。
- 反熵過程
一些數(shù)據(jù)存儲有后臺進(jìn)程不斷查找副本之間數(shù)據(jù)的差異,將任何缺少的數(shù)據(jù)從一個副本復(fù)制到另一個副本。與基于主節(jié)點復(fù)制分復(fù)制日志不同的是,反熵過程并不保證以特定的順序復(fù)制寫入,并且會引入明顯的同步滯后。
讀寫quorum
n個副本,寫入需要w個節(jié)點確認(rèn),讀取必須至少查詢r個節(jié)點,則只要w + r > n,讀取節(jié)點就一定包含最新值,然后根據(jù)版本號就可以確定過期值。不要把w和r當(dāng)作絕對的保證,而是一種靈活可調(diào)的讀取新值的概率,因為現(xiàn)實情況往往更加復(fù)雜
寬松的quorum和數(shù)據(jù)回傳
配置適當(dāng)?shù)膓uorum的數(shù)據(jù)庫系統(tǒng)可以容忍某些節(jié)點故障,也不需要進(jìn)行故障切換,同時還可以容忍某些節(jié)點變慢(不需要等待所有副本響應(yīng))。
在一個大規(guī)模集群中(節(jié)點數(shù)遠(yuǎn)大于n),客戶可能在網(wǎng)絡(luò)中斷期間還能連接到某些數(shù)據(jù)庫節(jié)點,但這些節(jié)點又不能夠滿足數(shù)據(jù)仲裁的那些節(jié)點,此時你可能面臨一個選擇:
- 如果無法達(dá)到w或r,將錯誤明確返回給客戶端;
- 只是將它們暫時寫入到一些可以訪問到的節(jié)點(這些節(jié)點并不在n個節(jié)點集合中);
第二種方案稱為“寬松的仲裁”(sloppy quorum):寫入和讀取仍然需要w和r個成功的響應(yīng),但包含了那些并不在先前指定的n個節(jié)點。一旦網(wǎng)絡(luò)問題解決,臨時節(jié)點需要把接收到的寫入全部發(fā)送到原始節(jié)點上,這就是所謂的“數(shù)據(jù)回傳”。
可以看出,sloppy quorum對于提高寫入可用性特別有用,只要有任何w個節(jié)點可用,數(shù)據(jù)庫就可以接收新的寫入。同時,這也意味著,即使?jié)M足w + r > n,也不能保證著讀取r個副本時一定能讀取到新值,因為新值可能被臨時寫入n之外的某些節(jié)點且尚未回傳。所以,sloppy quorum更像是為了數(shù)據(jù)持久性而設(shè)計的一個保證措施。
多數(shù)據(jù)中心操作
在一些數(shù)據(jù)庫系統(tǒng)中(Cassandra和Voldemort):副本的數(shù)量n是所有數(shù)據(jù)中心的節(jié)點總數(shù),每個客戶端的寫入都會發(fā)送到所有副本,但客戶端通常只會等待來自本地數(shù)據(jù)中心內(nèi)的quorum節(jié)點數(shù)的確認(rèn)。
而有些數(shù)據(jù)庫系統(tǒng)(如:Riak)則將客戶端與數(shù)據(jù)庫節(jié)點之間的通信限制在一個數(shù)據(jù)中心內(nèi),因此n描述的是一個數(shù)據(jù)中心內(nèi)的副本數(shù)量。集群之間跨數(shù)據(jù)中心的復(fù)制則在后臺異步運行,類似于多主節(jié)點復(fù)制風(fēng)格。
檢測并發(fā)寫
處理寫沖突
- 最后寫入者獲勝:
強制對寫請求排序,最后只有一個寫入值存活下來。
如何判斷兩個操作是否并發(fā)?
- Happens-before關(guān)系和并發(fā)
如果B知道A,或者依賴于A,或者以某種方式在A基礎(chǔ)上構(gòu)建,則稱操作A中操作B之前發(fā)生。所以,如果兩個操作都不在另一個之前發(fā)生,那么操作是并發(fā)的。(呃。。。)
如果一個操作發(fā)生在另一個操作之前,則后面的操作可以覆蓋較早的操作;如果屬于并發(fā),就需要解決潛在的沖突問題。
如何讓確定操作并發(fā)性,即兩個操作究竟屬于并發(fā)還是一個發(fā)生在另一個之前(依賴)?
- 服務(wù)器為每個主鍵維護(hù)一個版本號,每當(dāng)主鍵新值寫入時遞增版本號,并將新版本號于寫入的值一起保存;
- 當(dāng)客戶端讀取主鍵時,服務(wù)器將返回所有當(dāng)前值以及最新的版本號。且要求寫之前,必須先發(fā)生讀請求;
- 客戶端寫請求必須包含:之前讀帶的版本號、讀到的值和新值三者合并后的集合。寫請求的響應(yīng)和讀操作一樣;
- 當(dāng)服務(wù)器收到帶有特定版本號的寫入時,覆蓋該版本號或更低版本的所有值;如果一個寫請求沒有包含版本號,它將與所有其他寫入同時進(jìn)行,不會覆蓋任何已有值,其傳入的值將包含在后續(xù)讀請求的返回值列表中;
合并同時寫入的值
不舍棄并發(fā)寫入的值和舊值,而是將舊值和新值進(jìn)行合并。但是對于刪除操作,項目在刪除時不能簡單地從數(shù)據(jù)庫中刪除,系統(tǒng)必須保留一個對應(yīng)的版本號以恰當(dāng)?shù)臉?biāo)記該項目需要在合并時刪除(結(jié)合購物車案例)。
版本矢量
對每個副本的每個主鍵均定義一個版本號,每個副本在處理寫入時增加自己的版本號,并且跟蹤從其他副本看到的版本號,通過這些信息來指示要覆蓋哪些值、該保留哪些值。所有副本的版本號集合稱為版本矢量。
總結(jié)
以上是生活随笔為你收集整理的DDIA笔记——数据复制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第二讲 命令源码文件
- 下一篇: Docker基本组成 和 基本命令