MySQL數(shù)據(jù)遷移工具的設(shè)計與實現(xiàn)

一、背景

MySQL作為最流行的關(guān)系型數(shù)據(jù)庫產(chǎn)品之一,當(dāng)數(shù)據(jù)規(guī)模增大遭遇性能瓶頸時,最容易想到的解決方案就是分庫分表。無論是進(jìn)行水平拆分還是垂直拆分,第一步必然需要數(shù)據(jù)遷移與同步。由此可以衍生出一系列數(shù)據(jù)遷移過程中的需求:

1.原本一張表遷移到單庫多表(或多庫多表),這是最基本的需求;

2.原本單庫多表(或多庫多表)遷移到新的多庫多表(因表設(shè)計不合理、數(shù)據(jù)規(guī)模增大等原因?qū)е滦枰俅畏謳旆直恚?/p>

3.新表與舊表的表結(jié)構(gòu)可能不一致,如:類型表更(自增主鍵id由int改為bigint)、字段數(shù)量不一致(刪減、增加)、字段名稱變更等

4.字段映射,如:舊表中的多個字段映射為新表的一個字段,或舊表中的一個字段映射為新表的多個字段

5.增量數(shù)據(jù)的實時同步,以及當(dāng)涉及表結(jié)構(gòu)轉(zhuǎn)換時增量部分(binlog)如何方便地實現(xiàn)同樣的轉(zhuǎn)換

6.如何支持垂直拆分的數(shù)據(jù)遷移

7.MySQL到NewSQL的遷移(如:TiDB、CockroachDB)

8.異構(gòu)數(shù)據(jù)源的實時遷移,如:MySQL到HBase、MongoDB(關(guān)于異構(gòu)數(shù)據(jù)源的實時同步設(shè)計不在本文內(nèi)容范圍,后續(xù)將專題介紹)

9.遷移前后的數(shù)據(jù)一致性校驗

二、設(shè)計

為滿足以上需求,下面將從全量遷移和增量同步兩部分來說明MySQL數(shù)據(jù)遷移同步工具的設(shè)計與實現(xiàn)。

2.1 全量遷移

mysqldump是MySQL官方自帶的數(shù)據(jù)備份工具,也可以用于數(shù)據(jù)遷移,但不足之處是單線程處理,遷移大表時速度極慢,并且不支持寫入分庫分表。因此開源社區(qū)還開發(fā)了一個多線程的類似工具mydumper,性能有不少提升,但同樣不支持寫入分庫分表,也不支持字段的轉(zhuǎn)換。

接下來介紹下快速分片并行讀取MySQL表數(shù)據(jù)的做法:

1、 自動查找表的主鍵pk;

2、 查詢主鍵的最大值及最小值:max(pk),min(pk);

3、 對主鍵范圍分片,每個分片跨度1萬(即最多讀取1萬行數(shù)據(jù)),由此即可將整張表的查詢分成多個查詢分片:

第1個分片查詢條件為pk >= min(pk) AND pk < min(pk)+10000

第2個分片查詢條件為pk>= min(pk)+10000 AND pk < min(pk)+20000

第3個分片查詢條件為pk >= min(pk)+20000 AND pk < min(pk)+30000

以此類推。

以上分片查詢除了可以并行讀取之外,另外一個優(yōu)勢是失敗可恢復(fù),某個分片查詢失敗并不影響整體查詢的進(jìn)度,只需失敗重試即可。當(dāng)然也可以將所有分片持久化,即使程序異常退出,重啟后也可以恢復(fù),避免重新查詢?nèi)頂?shù)據(jù)。

2.2 增量同步

增量數(shù)據(jù)的讀取基于MySQL的binlog主從復(fù)制。在全量遷移之前首先獲取當(dāng)前MySQL的位點信息(FileName、Position),以便全量數(shù)據(jù)遷移完成之后從該位點繼續(xù)重放binlog。

三、實現(xiàn)

3.1 全量遷移


基于RxJava的觀察者(或生產(chǎn)者消費者)模式實現(xiàn)鏈?zhǔn)阶畲蠡⑿刑幚恚憾鄰埍聿⑿猩刹樵兎制?Query Split),然后由Source并行執(zhí)行查詢分片從MySQL中讀取數(shù)據(jù),然后統(tǒng)一由Sink Selector根據(jù)分庫分表的sharding字段及規(guī)則計算出每行數(shù)據(jù)所屬的slot(即應(yīng)該寫入到哪張分表),當(dāng)一個slot中的數(shù)據(jù)積累到一個batch size時會生成一個插入分片(Insert Split),最終由Sink并行地批量寫入對應(yīng)的目標(biāo)表中。

為了避免累積的數(shù)據(jù)過多造成GC壓力,slot超過一定時間后即使沒有累積到一個batch size也會生成Insert Split分發(fā)給Sink執(zhí)行寫入。此外還要考慮另外一個問題:當(dāng)生產(chǎn)者生產(chǎn)過快導(dǎo)致消費者來不及處理時,將會導(dǎo)致事件堆積,嚴(yán)重時還會OOM,即所謂的背壓(Backpressure)。幸好RxJava作為一個成熟的Reactive框架已經(jīng)對背壓處理有很好的支持,這也是為什么要基于RxJava來實現(xiàn)的重要原因之一。

3.2 增量同步


binlog的抽取使用了開源的Java類庫mysql-binlog-connector-java,與Canal相比更加輕量,源碼清晰易懂,不依賴其他第三方j(luò)ar包,也沒有那么多不需要的繁雜功能。

為了實現(xiàn)對binlog的字段轉(zhuǎn)換,采用了Apache開源的SQL引擎calcite來實現(xiàn):將binlog的每行數(shù)據(jù)根據(jù)原表的表結(jié)構(gòu)映射為一張內(nèi)存表,然后由calcite執(zhí)行SQL轉(zhuǎn)換后輸出結(jié)果。(PS:calcite當(dāng)前已被多個開源項目采用,Hive用calcite優(yōu)化查詢,F(xiàn)link的Streaming SQL基于calcite實現(xiàn),Kylin的查詢引擎也采用calcite)

3.3 數(shù)據(jù)校驗

因MySQL表的checksum與數(shù)據(jù)的行順序無關(guān),當(dāng)新表與舊表的表結(jié)構(gòu)相同并且數(shù)據(jù)不需要轉(zhuǎn)換時采用執(zhí)行CHECKSUM TABLE tbl_name查詢語句獲取每張新表和舊表的checksum,然后分別求和對比最終的checksum是否相同以此校驗數(shù)據(jù)是否一致。

當(dāng)新表與舊表存在字段類型變更、字段數(shù)量不一致、數(shù)據(jù)經(jīng)過轉(zhuǎn)換等會導(dǎo)致checksum發(fā)生變化時,采用排除有關(guān)字段,由遷移工具內(nèi)部只對剩余字段數(shù)據(jù)進(jìn)行checksum計算。Checksum算法可以選擇CRC32或Adler32,這兩種算法均采用Java自帶的實現(xiàn)類,默認(rèn)情況下使用Adler32因為其具有更快的計算效率。

四、總結(jié)

無論是分庫分表常規(guī)方案的實施,還是未來新一代分布式關(guān)系型數(shù)據(jù)存儲NewSQL的落地實踐,數(shù)據(jù)的遷移與同步都是必不可少的重要環(huán)節(jié)。畢竟,快速、準(zhǔn)確、平滑地完成數(shù)據(jù)遷移,便已成功了一半。

最后編輯于
?著作權(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)容