數據遷移的應用場景與解決方案Hamal

本文來自網易雲社區html


做者:馬進
mysql

跑男熱播,做爲兄弟團忠實粉絲,筆者也是一到週五就如打雞血樂不思蜀。

看着銀幕中一衆演員搞怪搞笑的浮誇演技,也時常感慨,這樣一部看似簡單真情流露的真人秀,必然飽含了許許多多臺前幕後工做者的辛苦汗水,若是把一部真人秀比做一個互聯網產品,那麼在銀幕中那些大明星就比如產品開發者:他們須要敏銳地把握觀衆的需求和口味,與終端用戶直接打交道。而燈光,道具,服裝,攝影這些就比如系統開發者,他們要盡一切努力知足產品開發者提出的需求,而且暴露給他們最簡潔直觀的接口,就像跑男同樣,把一切複雜實現包裝在幕後,留給衆演員一個簡單開闊的舞臺飆戲。

對於數據庫來講同樣如此,在數據容量日益膨脹的今天,單機數據庫的容量,IOPS等使用瓶頸已愈發明顯,在這樣的背景下,產品開發者每每面臨兩種選擇:第一是採用mongodb,hbase一類的NewSQL系統來突破單機的限制,但這種作法在產品需求不斷累加時每每弊大於利,由於任何一款NewSQL或NoSQL產品都沒法支持關係型數據庫那樣多的特性,例如事務,電商場景下必然會用到,例如支持豐富的adhoc查詢。爲了可以使用關係型數據庫的各種特性,又支持良好的擴展性和延展性,架構師們每每會傾向選擇第二種方案:分庫分表的關係型數據庫,將數據分分佈到不一樣的關係型數據庫裏,以突破單個數據庫的各種限制。可是使用分庫分表的方案會帶來另外一些問題,好比應用怎樣像使用普通關係型數據庫同樣使用分庫分表的系統?當前的分庫分表方案沒法知足業務需求時應該如何處理?這些問題就比如真人秀節目中演員們在表演時會遇到的各種障礙,須要咱們幕後工做者,也就是系統開發者經過中間件的方案來解決。

網易分佈式數據庫DDB從06年開始爲網易互聯網核心產品提供透明的分庫分表服務,10年風雨,可謂見證了各大產品興衰榮辱,在網易核心互聯網應用中基本都能看到DDB的身影。DDB的核心價值充分體現了系統開發者做爲幕後工做人員的良苦用心:應用開發者在使用DDB時,基本上像使用mysql同樣簡單。篇幅所限,DDB的內容沒法展開討論,由於本文的重點是另一套和DDB息息相關,一樣展示了幕後工做者核心價值的中間件系統:數據遷移工具Hamal。

Hamal是一套基於MySQL binlog實現的數據遷移、數據同步以及數據分發系統。DDB的存在爲咱們很好地解決了應用在使用分庫分表時的透明性問題,可是卻沒法解決數據分佈擴展後帶來的數據遷移問題,這裏就是Hamal大展拳腳之地了,另外,因爲Hamal擁有一套能夠擴展的接口,它也能夠在必定程度上解決數據同步和數據分發的問題。

咱們首先來看看數據遷移,數據同步和數據分發這三種應用場景。算法


數據遷移

數據遷移的需求是咱們團隊開發Hamal引擎的初衷,在DDB漫長的技術支持中,曾經遇到不少數據遷移需求,這種需求能夠歸爲兩大類:

庫的擴容縮容

數據庫的擴容縮容是分庫分表下一種很是典型的數據遷移場景,例如在DDB最先的應用產品博客項目中,出現過20個庫擴40個擴的案例。在hamal工具出現之前,應對這種擴容場景的解決方案是依賴MySQL原生的複製機制,具體過程很是複雜,由於增量數據的遷移中包含髒數據,在作切換後須要一段刪除增量髒數據的時間,所以實際的切換須要進行兩次。基本步驟爲:


sql

以一擴二的場景爲例,要擴容的原庫爲d1:mongodb

a) 全量複製:經過MySQLDump或者innobackup對要擴容的庫d1進行全量備份和恢復,恢復出來的庫爲r1, r2
b) 刪除全量髒數據:將r1,r2中不該該屬於本身的全量數據刪除,1擴2的場景下,這個過程會刪掉r1,r2兩個庫累加起來後的冗餘數據
c) 增量複製:將r1,r2分別做爲d1的slave進行增量數據同步,注意同步點要以備份時的binlog位置爲準
d) 增量追上切庫,改寫SQL:這個步驟其實涉及到三件事情,首先要等r1和r2做爲slave追上d1的binlog位置,而後在DDB的中間件層將數據分佈表從d1改寫爲r1和r2,因爲數據分佈表已經發生變化,新的查詢和更新都會在r1,r2上進行,可是r1,r2上增量數據是「不乾淨的」,所以須要在下發的全部查詢中增長對髒數據的過濾條件。在之前的DDB版本中,會爲每一個將來可能須要遷移的表增長一個默認的bucketNo字段,每一個表有多少個bucketNo在建表之初必須指定,而且將來不能更改,DDB全部表的分區字段通過hash後會對應到bucketNo上,再由bucketNo與實際的數據庫節點對應,即所謂的均衡策略。在數據分佈表由d1切爲r1,r2後,須要在全部查詢後增長bucketNo not in(other bucketNos in d1)的條件,例如對下發給r1的查詢後必須增長where bucket No not in (other bucketNos in r2)。這三個過程嚴絲合縫,由DDB內部完成,應用毫無感知。
e) 刪除增量髒數據:在完成數據分佈表切換後,DDB需啊啓動一個刪除增量髒數據的過程,刪除的方法與第二個步驟相似。
f) 撤銷SQL改寫:在完成刪除增量髒數據的過程後,再也不須要DDB層爲全部查詢增長過濾髒數據的條件,此後撤銷全部查詢SQL的改寫。

以上6步構成了老版本DDB的數據遷移方案,這個解決方案優點是對外部依賴少,整個過程除了改寫SQL外,只須要一個額外的刪除髒數據的進程,全量複製和增量複製都是經過MySQL自帶的方案解決,可是缺陷也是很是明確的:
數據庫

  • 須要依賴bucketNo這個字段,事實上這個字段僅僅爲了數據遷移這個應用場景而生,在不少不會有數據遷移的表中初始並不會有這個字段緩存

  • 改寫下發SQL和刪除增量髒數據的過程發生在數據分佈表切換後,會對線上業務產生必定的性能影響數據結構

  • 這個方案僅對1擴2,2擴3規則適用,對一些比價簡單的擴容場景好比9擴10,因爲在改寫SQL環節實現會很麻煩,並不支持架構

  • 整個過程複雜且慢,須要DBA對原理熟悉方能操做併發

老的數據遷移方案的實施過程較爲繁瑣,雖然咱們在很大程度上實現了自動化,可是一旦出問題處理起來會比較麻煩,例如在切換在引入Hamal數據遷移工具後,一切的困難迎刃而解。咱們來看看一樣的數據遷移需求,H使用Hamal須要經歷怎樣的過程:


a) 全量複製:Hamal的全量複製方案有兩種,第一種是使用MySQLDump或Mydump對原數據庫進行邏輯備份,再由isql或DDB的DBI模塊進行數據導入,因爲全量數據經過isql或DBI導入後數據會通過數據分佈表從新分佈,不會產生髒數據,也就省去了以後的刪除全量髒數據的過程。第二種全量複製方案是在Hamal engine內部啓動一個全量複製線程定批對原庫數據進行數據拷貝,這個過程經過對數據批量加鎖能夠與增量複製併發進行,這個過程與percona的在線修改表結構方案中的全量複製很是相似。不管是哪一種方案,因爲都通過數據重分佈,不會向新庫引入髒數據。

b) 增量複製:Hamal的增量複製方案是使用Hamal engine模擬MySQL slave向原庫拉取binlog,通過自身模塊的解析生成相應的數據遷移SQL,再逐條經過DBI模塊apply到新庫中,這樣一樣能夠在增量複製的同時避免引入髒數據
c) 增量追上切庫:因爲增量複製也不會引入髒數據,也就避免了須要刪除增量髒數據和改寫SQL,在發覺binlog位置追上後通過短暫的數據分佈表切換便可完成數據遷移過程。
基於Hamal的DDB數據遷移方案與老方案相比,步驟節省了一半,整個過程理解起來也比較容易,最重要的是它徹底解決老方案的4點缺陷:遷移過程無需依賴bucketNo字段;由於沒有改寫SQL和刪除增量髒數據,也不會對線上應用產生任何不利影響;因爲全量和增量複製過程原理都是通過DDB的數據分佈表來完成,且沒有SQL改寫,也就沒有了對1擴2,2擴3規則的限制;整個流程理解和操做起來門檻下降不少。

另外,Hamal engine基於一套有向無環圖算法實現了快速的並行複製,它的增量複製過程自己就比MySQL原生的複製過程快不少,可是使用Hamal的並行複製算法具備兩個限制,一是必須基於row格式binlog,二是全部遷移表必須含有主鍵或惟一性索引。這兩個限制是由算法原理自己決定的,原理會在下文展開。首先row格式binlog自己在使用上比Statement格式更加嚴謹,目前網易RDS中全部MySQL binlog都是基於row格式的,至於每張表必須含有主鍵或惟一性索引的要求也正是DBA對產品開發者耳提面命的要求,所以這兩個限制能夠忽略不計。

表的數據重分佈
表的數據重分佈與擴容縮容本質上是同樣的,在應用場景上體現爲表更換分區字段,或更換均衡策略。以更換分區字段爲例,更換分區字段後,全部數據都要從新通過新的數據分佈表來進行分佈。表的數據重分佈在需求上其實更爲常見,好比一個項目初期選擇分區字段上沒考慮清楚,致使數據增加後數據分佈不均勻,就要考慮更換一個更加均衡的分區字段,又如業務想對個別表作存儲隔離,就須要把這個表從當前共享均衡策略中遷移到一個隔離的均衡策略裏。
在DDB老的數據遷移方案中,並無針對表的遷移方案,在實際操做過程當中,通常DBA會根據場景的不一樣本身設計方案,多多少少都會要求產品方容忍必定的停服時間,而在引入Hamal的解決方案後,表的數據重分佈也迎刃而解。
由於Hamal的全量複製和增量複製都是由第三方工具完成,能夠高度定製化。在Hamal解決方案中,表的數據遷移與庫的擴容縮容並無本質區別,只是在全量複製和增量複製時過濾數據的方式不一樣。


數據同步
與DDB的數據遷移場景相比,數據庫之間的數據同步需求更加常見,因爲MySQL自帶的replication原生複製方案,尤爲最近幾個版本的並行複製效果尚可,你們每每會傾向使用MySQL自帶的數據同步功能。可是MySQL基於replication的數據同步方案有三個限制:

  1. MySQL原生複製只能作到點到點,沒法點到組

  2. MySQL原生複製依賴MySQL底層,上層沒法干預,同步方式沒法定製

  3. 雖然新版本MySQL並行複製速度尚可,但在跨機房同步等場景中依然不夠可觀

在使用DDB的業務場景中,經常須要用到點到組的數據同步,最經典的莫過於「好友關係問題」和「買家賣家問題」。以易信的好友關係爲例,好友表有兩個字段,分別爲userId和friendId,業務的查詢需求能夠分爲如下兩類:

  1. 查詢我有哪些好友

  2. 查詢我是哪些人的好友

「查詢個人好友」場景比較多,會佔主要部分,「查詢我是哪些人的好友」用於判斷評論可見性和好友推薦等場景,請求量也不小,在這種狀況下,選擇userId仍是friendId做爲分區字段成爲一個難題,不管選擇哪一個字段都會顧此失彼。在這種情況下,咱們的推薦作法是使用冗餘表,假設主表爲friendship,主鍵和分區字段爲userId,爲friendship建冗餘表friendship_reverse,主鍵和分區字段爲friendId,這樣「查詢個人好友」時走表friendship,「查詢我是哪些人的好友」時走表friendship_reverse。以下圖:


爲了保證業務的正確性,須要確保friendship表和friendship_reverse表數據嚴格一致,若是在業務層作事務性雙寫,因爲兩表分區字段不一樣,雙寫會成爲一個分佈式事務,目前任何基於兩階段協議實現的分佈式事務都沒法作到單機事務保證嚴格的ACID,所以並不建議在採用雙寫的方式解決冗餘問題。

推薦的作法是經過第三方的數據同步中間件來保證冗餘表friendship_reverse的數據一致性,對於MySQL原生複製,因爲分區字段不一樣,由一個源數據庫產生的數據可能要同步到不一樣的終點數據庫中,即所謂的「點到組」,另外,同步的兩個表名自己也並不相同,在這裏MySQL的原生複製功能沒法定製的功能體現無疑。
Hamal engine能夠很好地解決這類數據同步問題,咱們須要作的是部署與源數據庫實例數量相同的Hamal engine進程,根據同步條件稍做配置。每一個hamal engine會以一個slave的身份向一個源數據庫拉取binlog,根據同步條件過濾binlog事件,再根據apply配置生成相應SQL應用到目標數據庫中,因爲friendship_reverse在DDB中有已經定義好的數據分佈表,hamal在apply過程當中並不須要知道目的節點信息,只要經過DDB中間件去apply便可完成透明的數據路由,這也是Hamal和DDB配合使用的妙處之一。
除了業務上的冗餘表外,跨機房的數據同步也是Hamal能夠大展拳腳的應用場景之一,在2015年的中國數據庫技術大會上,阿里巴巴集團第一次向外界分享了基於DRC的異步多活的多機房數據同步解決方案,而DRC的核心就是一個個旁路的拉取binlog並進行定製化跨機房傳輸並apply binlog的進程。在這方面hamal與DRC的設計殊途同歸,將來能夠基於hamal來定製咱們本身的跨機房數據同步方案。

數據分發

數據分發是一個較爲偏向業務的應用場景,例如經過數據分發更新緩存,建立更新索引等。由Hamal engine拉取源數據庫的binlog,根據配置生成相應的下游任務,這個下游任務能夠是向消息隊列裏插入一條數據,也能夠直接更新緩存,調用RPC等。阿里相似的「精衛」系統已在線上爲淘寶各類業務線提供了幾年的穩定服務。


Hamal特性與原理簡析  
 
在介紹DDB數據遷移場景中,對Hamal工做原理已作過簡要介紹,Hamal分爲Hamal engine和Hamal admin兩大部件,Hamal engine是真正負責數據遷移,數據分發和數據同步的進程,而hamal admin是負責管理hamal engine,調度任務的外部組件集合。目前Hamal engine已經開發完成,而且開始在實際的數據遷移場景中發揮做用。Hamal engine具備的特性以下:  

  1. 支持行級和事務級並行複製,追趕binlog速度遠快於MySQL原生複製

  2. 支持斷點續傳,在源端或目的端出現問題後,能夠暫停任務,修復問題後恢復任務

  3. 保證數據一致性,其中行級事務並行複製適用於遷移場景,只保證最終一致性,在犧牲徹底實時一致性的同時知足了高速的需求

Hamal engine的特性在於快和冪等,快保證了hamal在數據遷移場景下很好地縮短任務週期,也有益於數據同步場景下的同步實時性,冪等則保證了hamal engine只須要定時記錄一個事務記錄點,便可在不破壞數據一致性的前提下重啓任務。  
hamal實現冪等的核心思想是使用replace代替insert和update,保證數據最終的值必定會生效在目標數據庫裏。使用replace的前提是要保證同一份數據的操做必須排隊執行。  
同時,爲了保證數據的正確性,Hamal engine也必須保證即使是在並行複製下,同一份數據的apply也必須按照在binlog位置中的順序來執行,這個過程相似於MySQL中的「行鎖」,只不過Hamal engine中沒有保存真實的數據結構,也難以實現一套相似於MySQL的完整鎖體系,Hamal engine內部的「行鎖」機制是經過一個有向無環圖算法,爲一份數據在binlog不一樣位置上的修改建議依賴關係,能夠理解爲同一行上的修改經過咱們的有向無環圖算法造成了一個執行隊列,這個隊列中只有上一個節點執行完成纔可能觸發下一個節點的執行。  
 
Hamal engine的核心組件以下所示:  
               

其中,Extractor模塊經過MySQL協議向源端MySQL拉取binlog,並存儲到本地盤或雲硬盤的relaylog中,這方面與MySQL原生複製中的IO thread徹底一致。Poller是一個獨立線程,會隨時讀取relaylog中最新的二進制數據,並轉換爲Hamal能夠處理的Event,根據配置的不一樣,一個event能夠表明一行數據的更改,也能夠表明一個事務。一個event在被回放以前首先會進入一個叫作EventGraph的數據結構中計算與當前系統中全部event的依賴關係,對同一份數據,若是發現有前後更改的衝突,EventGraph會按照依賴關係將後面的更改先存儲起來,當以前的更改回放完成後,再觸發以後的更改。若是一個event再也不依賴系統中任意其餘event,這個event將會被丟入SlaveQueue,並最終被某個applier線程回放掉。  
 
對於一張表,表上全部惟一性索引,包括主鍵都會對應一個EventGraph,由於只有經過惟一性衝突才能判斷數據之間是否存在依賴。對於惟一健值更改的狀況,還須要將更改分裂爲delete和replace,而且分別將delete和replace的鍵值拿出來計算依賴。  
 
 總結  
 
大數據時代,數據每每表明了一個互聯網企業的核心價值,怎樣去玩數據也是成爲各個企業重點關注的問題。在這個背景下,相似於Hamal這種基於MySQL的數據遷移,數據同步和數據分發解決方案能夠發揮不俗的價值,一樣類型的系統,在阿里就有好幾套。  
 
咱們對Hamal的指望,是以hamal engine實現一個核心架構和一套簡潔的接口,在爲DDB數據遷移,MySQL數據同步,分發等場景分別建立不一樣的插件實現。最後經過Hamal admin實現各類解決方案的串聯和自動化,由於Hamal是一個獨立於MySQL的中間件系統,咱們能夠在上面充分發揮"第三方"的優點,目前高速的並行複製和斷點續傳都是Hamal相比於其餘數據複製方案的優點。將來,咱們也能夠在Hamal上定製更多的高可用功能,從而將它升級爲一個多機房的數據同步解決方案。



本文來自網易雲社區,經做者馬進受權發佈 


相關文章:
【推薦】 本身動手寫ImpalaUDF
【推薦】 網易對象存儲NOS圖牀神器

相關文章
相關標籤/搜索