您好,登錄后才能下訂單哦!
從Zeppelin:一個(gè)分布式KV存儲(chǔ)平臺(tái)之概述的介紹中我們知道元信息節(jié)點(diǎn)Meta以集群的形式向整個(gè)Zeppelin提供元信息的維護(hù)和提供服務(wù)。可以說(shuō)Meta集群是Zeppelin的大腦,是所有元信息變化的發(fā)起者。每個(gè)Meta節(jié)點(diǎn)包含一個(gè)Floyd實(shí)例,從而也是Floyd的一個(gè)節(jié)點(diǎn),Meta集群依賴Floyd提供一致性的內(nèi)容讀寫(xiě)。本文將從角色、線程模型、數(shù)據(jù)結(jié)構(gòu)、選主與分布式鎖、集群擴(kuò)容縮容及成員變化幾個(gè)方面詳細(xì)介紹,最后總結(jié)在Meta節(jié)點(diǎn)的設(shè)計(jì)開(kāi)發(fā)過(guò)程中帶來(lái)的啟發(fā)。
從上圖可以看出Meta集群的中心地位:
向Client及Node Server提供當(dāng)前的元信息,包括分片副本信息,Meta集群成員信息等;
保持與Node Server的心跳檢測(cè),發(fā)現(xiàn)異常時(shí)進(jìn)行切主;
接受并執(zhí)行運(yùn)維命令,完成相應(yīng)的元信息變化,包括擴(kuò)容、縮容、創(chuàng)建Table、刪除Table等;
相對(duì)于存儲(chǔ)節(jié)點(diǎn),元信息節(jié)點(diǎn)的線程模型比較簡(jiǎn)單:
處理請(qǐng)求的Dispatch線程和Worker線程;
修改Floyd的Update線程,Update線程是唯一的Floyd修改者。所有的元信息修改需求都會(huì)通過(guò)任務(wù)隊(duì)列轉(zhuǎn)交給Update線程。同時(shí)為了減輕Floyd的寫(xiě)入壓力,這里采用了延時(shí)批量提交的方式;
Condition線程用來(lái)等待Offset條件,一些元信息修改操作如SetMaster,擴(kuò)容及縮容,需要等到分片副本的主從Binlog Offset追齊時(shí)才能執(zhí)行,Meta從與Node之間的心跳中得到Offset信息,Condition線程不斷的檢查主從的Offset差距,僅當(dāng)追齊時(shí)通知Update線程完成對(duì)應(yīng)修改;
Cron線程執(zhí)行定時(shí)任務(wù),包括檢查和完成Meta主從切換、檢查Node存活、Follower Meta加載當(dāng)前元信息、執(zhí)行數(shù)據(jù)遷移任務(wù)等。
為了完成上述任務(wù),Meta節(jié)點(diǎn)需要維護(hù)一套完整的數(shù)據(jù),包括Node節(jié)點(diǎn)心跳信息、Node節(jié)點(diǎn)Offset信息、分片信息、Meta成員信息、擴(kuò)容遷移信息等。由于一致性算法本身限制,我們需要盡量降低對(duì)Floyd的訪問(wèn)壓力,因此并不是所有這些數(shù)據(jù)都需要直接維護(hù)在Floyd中。Zeppelin根據(jù)數(shù)據(jù)的重要程度、訪問(wèn)頻率及是否可恢復(fù)進(jìn)行劃分,僅僅將低頻訪問(wèn)且不易恢復(fù)的數(shù)據(jù)記錄在Floyd中。
上圖所示是Meta節(jié)點(diǎn)所維護(hù)數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)及存儲(chǔ)方式,可以看出,除了一致性庫(kù)Floyd中記錄的數(shù)據(jù)外,Meta還會(huì)在內(nèi)存中維護(hù)對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu),內(nèi)存數(shù)據(jù)結(jié)構(gòu)依賴Floyd中的數(shù)據(jù),重新組織并提供更方便訪問(wèn)的接口。從所完成的任務(wù)來(lái)看,主要包括三個(gè)部分:
對(duì)應(yīng)內(nèi)存數(shù)據(jù)結(jié)構(gòu)InfoStore,InfoStore依賴Floyd中的數(shù)據(jù),包括:
當(dāng)前元信息的版本號(hào)Epoch,每次元信息的變化都會(huì)對(duì)Epoch加一;
數(shù)據(jù)分片副本的分布及主從信息Tables;
存儲(chǔ)節(jié)點(diǎn)地址及存活信息Nodes;
Meta集群自己的成員信息Members;
InfoStore重新組織這些數(shù)據(jù),對(duì)外提供方便的查詢和修改接口;除此之外InfoStore還會(huì)維護(hù)一些頻繁修改但可以恢復(fù)的數(shù)據(jù):
存儲(chǔ)節(jié)點(diǎn)上次心跳時(shí)間:宕機(jī)后丟失,可以通過(guò)Floyd中的Nodes信息及恢復(fù)時(shí)的當(dāng)前時(shí)間恢復(fù),注意這里使用恢復(fù)時(shí)的當(dāng)前時(shí)間相當(dāng)于延長(zhǎng)的存儲(chǔ)節(jié)點(diǎn)的存活;
存儲(chǔ)節(jié)點(diǎn)的分片Binlog偏移信息:Meta需要這些信息來(lái)決定副本的主從切換,宕機(jī)恢復(fù)后可以從Node的心跳中獲得,這也就要求Node在重新建立心跳連接后的第一個(gè)包需要攜帶全量的Binlog偏移信息。
對(duì)應(yīng)內(nèi)存數(shù)據(jù)結(jié)構(gòu)MigrateRegister,負(fù)責(zé)遷移過(guò)程的注冊(cè)和提供,這部分內(nèi)容將在稍后的集群擴(kuò)容、縮容章節(jié)中詳細(xì)介紹。
Meta以集群的方式提供服務(wù),Leader節(jié)點(diǎn)完成寫(xiě)操作,F(xiàn)ollower分擔(dān)讀壓力,節(jié)點(diǎn)之間依賴Floyd保證一致,從而實(shí)現(xiàn)Meta集群的高可用。內(nèi)存數(shù)據(jù)結(jié)構(gòu)Election負(fù)責(zé)節(jié)點(diǎn)異常時(shí)的選主,依賴Floyd提供的Lock接口及其中的Election相關(guān)數(shù)據(jù)。這部分內(nèi)容將在稍后的選主與分布式鎖章節(jié)中詳細(xì)介紹。
Meta集群中的節(jié)點(diǎn)分為L(zhǎng)eader和Follower兩種角色:
所有的寫(xiě)操作及心跳都會(huì)重定向到Leader,Leader將需要修改Floyd的請(qǐng)求封裝為T(mén)ask,加入等待隊(duì)列,批量延時(shí)的寫(xiě)入Floyd,并更新本地的內(nèi)存數(shù)據(jù)結(jié)構(gòu)。
Follower定時(shí)檢查Floyd中的元信息,如果變化則加載并修改本地內(nèi)存數(shù)據(jù)結(jié)構(gòu),并對(duì)外提供元信息的查詢操作。
因此我們需要一種機(jī)制來(lái)選主,并且每個(gè)Leader需要一個(gè)定長(zhǎng)的租約時(shí)間,在租約時(shí)間內(nèi)集群不會(huì)選擇其他Meta節(jié)點(diǎn)作為新的Leader,相當(dāng)于犧牲一定的可用性來(lái)優(yōu)化讀性能。選主問(wèn)題是典型的分布式鎖的應(yīng)用,獲得分布式鎖的節(jié)點(diǎn)即為主。我們認(rèn)為分布式鎖是由三層相互獨(dú)立的問(wèn)題組成的,如下圖左邊所示,自下而上分別是一致性(Consistency),鎖的實(shí)現(xiàn)(Lock)及鎖的使用(Usage of Lock)。其中Consistency是為了高可用,Lock提供了互斥的加鎖的機(jī)制,而Usage of Lock部分通常有兩種實(shí)現(xiàn):
嚴(yán)謹(jǐn)實(shí)現(xiàn):加鎖時(shí)返回Sequence,這個(gè)Sequence自增,獲得鎖的節(jié)點(diǎn)之后的所有操作的受體都必須檢查這個(gè)Sequence以保證操作在鎖的保護(hù)中;
簡(jiǎn)單實(shí)現(xiàn):節(jié)點(diǎn)在嘗試加鎖時(shí)需要提供一個(gè)時(shí)間,鎖服務(wù)保證這個(gè)時(shí)間內(nèi)不將鎖給其他節(jié)點(diǎn)。使用者需要自己保證所有的操作能在這個(gè)時(shí)間內(nèi)完成。這個(gè)方法雖然不嚴(yán)謹(jǐn)?shù)欠浅:?jiǎn)單易用,Zeppelin的Meta集群采用的正是這種方式。
如上圖右邊部分顯示了這三部分在Meta中的對(duì)應(yīng)關(guān)系:
Consistency我們依賴Floyd實(shí)現(xiàn)的Raft,同時(shí)Raft對(duì)外提供了細(xì)粒度的鎖接口以及存儲(chǔ)數(shù)據(jù)的Set、Get接口;
依賴Raft提供的接口,Meta實(shí)現(xiàn)了自己的粗粒度鎖Coarse-Lock,簡(jiǎn)單的說(shuō),通過(guò)Set Get接口存儲(chǔ)或查詢當(dāng)前Leader的地址信息及上次刷新時(shí)間;并通過(guò)Floyd的細(xì)粒度鎖保護(hù)互斥的訪問(wèn);Leader定時(shí)刷新自己的時(shí)間,F(xiàn)ollower發(fā)現(xiàn)Leader超時(shí)后取而代之。Coarse-Lock層實(shí)現(xiàn)了Meta集群鎖需要的Election。
利用Coarse-Lock,Meta實(shí)現(xiàn)了自己的高可用。Cron線程中不斷觸發(fā)當(dāng)前節(jié)點(diǎn)檢查并在需要的時(shí)候嘗試選主。
這里需要說(shuō)明的是,相對(duì)于Fine-Lock而言,Coarse-Lock加鎖的時(shí)間更長(zhǎng),響應(yīng)的鎖的遷移也會(huì)帶來(lái)更大的成本。比如主從鏈接的重新建立,任務(wù)隊(duì)列的丟棄及清空,Meta工作線程的切換等。因此我們希望下層Lock抖動(dòng)盡量不要影響上層的主從關(guān)系,針對(duì)這點(diǎn)Meta中設(shè)計(jì)了如下兩種機(jī)制:
Meta主從關(guān)系與Floyd主從關(guān)系解耦,即使Floyd主從變化,依然有可能對(duì)上層Meta集群透明;
引入Jeopardy階段,正常運(yùn)行過(guò)程中,Meta會(huì)記錄當(dāng)前的Leader信息,當(dāng)Floyd由于網(wǎng)絡(luò)或節(jié)點(diǎn)異常無(wú)法服務(wù)時(shí),Meta層會(huì)進(jìn)入Jeopardy階段中,Jeopardy使得Meta節(jié)點(diǎn)在一定的時(shí)間內(nèi)依然保持主從關(guān)系不變。這個(gè)時(shí)間就是上面提到的為了讀優(yōu)化給Leader的Lease。之所以能夠這么做,正是由于Zeppline的設(shè)計(jì)中盡量減少對(duì)Meta集群作為中心節(jié)點(diǎn)的依賴,從而可以接受Meta集群短時(shí)間的不可用。
Zeppelin作為存儲(chǔ)集群,考慮到資源利用及業(yè)務(wù)變化,不可避免的需要進(jìn)行集群的擴(kuò)容、縮容或平衡操作。下圖是簡(jiǎn)單的擴(kuò)容操作示例,三個(gè)存儲(chǔ)節(jié)點(diǎn)Node1,Node2,Node3,負(fù)責(zé)分片P1,P2,P3的九個(gè)主從副本。這時(shí)Node4加入,需要將P1的Master副本和P3的Slave副本遷移過(guò)去。
針對(duì)這類問(wèn)題,主要有如下訴求:
持續(xù)時(shí)間可能很長(zhǎng),過(guò)程中無(wú)人工介入;
保證數(shù)據(jù)正確;
減少線上服務(wù)無(wú)感知;
不顯著增大Meta負(fù)擔(dān),包括資源使用和代碼復(fù)雜度;
Meta節(jié)點(diǎn)異?;蚓W(wǎng)絡(luò)異常后可從斷點(diǎn)恢復(fù);
容忍N(yùn)ode狀態(tài)變化;
方便暫停、取消,可以獲取狀態(tài)及當(dāng)前進(jìn)度;
為了很好的解決這個(gè)問(wèn)題,我們先進(jìn)行子問(wèn)題的推導(dǎo)及切割:
擴(kuò)容、縮容及平衡,其實(shí)都是將多個(gè)分片從源節(jié)點(diǎn)移動(dòng)到目的節(jié)點(diǎn);
遷移一個(gè)分片,可以拆分為增加新的Slave分片,等待數(shù)據(jù)同步,切換并刪除原分片三個(gè)步驟。
上圖所示是Zeppelin的擴(kuò)容、縮容及平衡機(jī)制:
客戶端命令行工具中將擴(kuò)容(Expand),縮容(Shrink)及平衡(Balance)操作,通過(guò)Zeppelin的數(shù)據(jù)分布算法DPRD轉(zhuǎn)化為初始狀態(tài)(Init State)和一組DIFF集合,每一個(gè)DIFF項(xiàng)指定一個(gè)分片副本及其要遷移的源節(jié)點(diǎn)、目的節(jié)點(diǎn);
Init State及DIFF集合傳遞給Meta Leader節(jié)點(diǎn)的Migrate Registor模塊,檢查Init State并將DIFF集合寫(xiě)入Floyd;
Cron線程定時(shí)獲取一定量的DIFF項(xiàng),順序執(zhí)行每個(gè)DIFF項(xiàng);
生成添加新的從副本的UpdateTask1交給Update線程盡快執(zhí)行,同時(shí)設(shè)置狀態(tài)將該分片緩寫(xiě)或阻寫(xiě);
生成ConditionTask交給Condition線程,ConditionTask中包括一個(gè)Offset條件和一個(gè)切換副本的UpdateTask2,這個(gè)Offset條件通常指源節(jié)點(diǎn)和目的節(jié)點(diǎn)偏移量一致;
Condition線程等待滿足Offset條件后將對(duì)應(yīng)的UpdateTask2交給Update線程盡快執(zhí)行;
完成必要的狀態(tài)改變后,將對(duì)應(yīng)的DIFF項(xiàng)從Register中刪除,并繼續(xù)取出新的DIFF執(zhí)行,直到全部完成。通過(guò)這種方式,任何時(shí)候Meta節(jié)點(diǎn)宕機(jī),新Leader都可以從Floyd中獲得DIFF并繼續(xù)操作。
通常Meta集群是一個(gè)3或5個(gè)節(jié)點(diǎn)的一致性集群,有時(shí)我們需要改變Meta集群的成員,增加、刪除或替換。Meta集群的成員變化依賴下層的Floyd,F(xiàn)loyd提供每次一個(gè)節(jié)點(diǎn)變化的Membership Change方式,詳細(xì)算法見(jiàn)CONSENSUS: BRIDGING THEORY AND PRACTICE。
Floyd的成員變化后,Meta集群對(duì)應(yīng)修改自己的內(nèi)存數(shù)據(jù)結(jié)構(gòu)Membership,同時(shí)元信息Epoch加一,從而整個(gè)集群的Node及Client都能從新拉取的元信息中得知新的Membership。
將中心節(jié)點(diǎn)的責(zé)任分散到大量的Node和Client上,從而減輕對(duì)中心節(jié)點(diǎn)的依賴:
心跳:由Node發(fā)起并檢查鏈接狀態(tài);
元信息提供:Node及Client發(fā)現(xiàn)主動(dòng)拉去,而不是由Meta分發(fā)。同時(shí)Node還會(huì)在訪問(wèn)錯(cuò)誤節(jié)點(diǎn)時(shí),給Client返回kMove消息來(lái)幫助Client不通過(guò)Meta即可得到部分需要的元信息數(shù)據(jù)。
比如,我們通過(guò)批量延遲提交Flody的方法,將一段時(shí)間以內(nèi)的修改操作歸并起來(lái),變成一次對(duì)Floyd的訪問(wèn)。將請(qǐng)求數(shù)級(jí)別修改頻次變?yōu)槊棵氤?shù)次。
同樣為了減少Floyd的壓力,我們不會(huì)將所有數(shù)據(jù)存儲(chǔ)到Floyd中,那么有些只在內(nèi)存中維護(hù)的數(shù)據(jù)就需要在服務(wù)恢復(fù)時(shí)恢復(fù)出來(lái),恢復(fù)時(shí)的數(shù)據(jù)來(lái)源可以包括:
持久化數(shù)據(jù)(盡量少);
外部請(qǐng)求中攜帶,比如Node的Offset信息從心跳中恢復(fù);
估計(jì)值,比如Meta中的Node存活時(shí)間是直接用恢復(fù)的當(dāng)前時(shí)間的;
Meta中的所有操作都是無(wú)重試可重入的,指所有步驟的失敗不直接進(jìn)行重試,而是做一些狀態(tài)恢復(fù)后丟棄,依賴外部的二次操作來(lái)完成,也就要求所有的的操作都是可重入的,這樣做帶來(lái)的好處是:
處理清晰簡(jiǎn)單,所有發(fā)生錯(cuò)誤的地方可以直接丟棄任務(wù);
上層更好估計(jì)操作完成需要的時(shí)間,從而向分布式鎖或Node作出時(shí)間上的保證。
https://github.com/Qihoo360/zeppelin
https://github.com/PikaLabs/floyd
https://ramcloud.stanford.edu/~ongaro/thesis.pdf
http://baotiao.github.io/2017/09/12/distributed-lock/
http://catkang.github.io/2018/01/07/zeppelin-overview.html
http://catkang.github.io/2018/01/07/zeppelin-overview.html
https://whoiami.github.io/DPRD
原文鏈接:https://mp.weixin.qq.com/s/eCDWqgRG-FmWFkK6cgzm4w
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。