一洋侨、背景
MySQL作為最流行的關系型數(shù)據(jù)庫產品之一矾瑰,當數(shù)據(jù)規(guī)模增大遭遇性能瓶頸時,最容易想到的解決方案就是分庫分表。無論是進行水平拆分還是垂直拆分或听,第一步必然需要數(shù)據(jù)遷移與同步探孝。由此可以衍生出一系列數(shù)據(jù)遷移過程中的需求:
1.原本一張表遷移到單庫多表(或多庫多表),這是最基本的需求誉裆;
2.原本單庫多表(或多庫多表)遷移到新的多庫多表(因表設計不合理顿颅、數(shù)據(jù)規(guī)模增大等原因導致需要再次分庫分表)
3.新表與舊表的表結構可能不一致,如:類型表更(自增主鍵id由int改為bigint)足丢、字段數(shù)量不一致(刪減粱腻、增加)、字段名稱變更等
4.字段映射斩跌,如:舊表中的多個字段映射為新表的一個字段绍些,或舊表中的一個字段映射為新表的多個字段
5.增量數(shù)據(jù)的實時同步,以及當涉及表結構轉換時增量部分(binlog)如何方便地實現(xiàn)同樣的轉換
6.如何支持垂直拆分的數(shù)據(jù)遷移
7.MySQL到NewSQL的遷移(如:TiDB耀鸦、CockroachDB)
8.異構數(shù)據(jù)源的實時遷移柬批,如:MySQL到HBase、MongoDB(關于異構數(shù)據(jù)源的實時同步設計不在本文內容范圍袖订,后續(xù)將專題介紹)
9.遷移前后的數(shù)據(jù)一致性校驗
二氮帐、設計
為滿足以上需求,下面將從全量遷移和增量同步兩部分來說明MySQL數(shù)據(jù)遷移同步工具的設計與實現(xiàn)洛姑。
2.1 全量遷移
mysqldump是MySQL官方自帶的數(shù)據(jù)備份工具上沐,也可以用于數(shù)據(jù)遷移,但不足之處是單線程處理楞艾,遷移大表時速度極慢参咙,并且不支持寫入分庫分表。因此開源社區(qū)還開發(fā)了一個多線程的類似工具mydumper硫眯,性能有不少提升蕴侧,但同樣不支持寫入分庫分表,也不支持字段的轉換两入。
接下來介紹下快速分片并行讀取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)勢是失敗可恢復,某個分片查詢失敗并不影響整體查詢的進度番舆,只需失敗重試即可酝碳。當然也可以將所有分片持久化,即使程序異常退出恨狈,重啟后也可以恢復疏哗,避免重新查詢全表數(shù)據(jù)。
2.2 增量同步
增量數(shù)據(jù)的讀取基于MySQL的binlog主從復制禾怠。在全量遷移之前首先獲取當前MySQL的位點信息(FileName返奉、Position),以便全量數(shù)據(jù)遷移完成之后從該位點繼續(xù)重放binlog吗氏。
三芽偏、實現(xiàn)
3.1 全量遷移
基于RxJava的觀察者(或生產者消費者)模式實現(xiàn)鏈式最大化并行處理:多張表并行生成查詢分片(Query Split),然后由Source并行執(zhí)行查詢分片從MySQL中讀取數(shù)據(jù)弦讽,然后統(tǒng)一由Sink Selector根據(jù)分庫分表的sharding字段及規(guī)則計算出每行數(shù)據(jù)所屬的slot(即應該寫入到哪張分表)污尉,當一個slot中的數(shù)據(jù)積累到一個batch size時會生成一個插入分片(Insert Split),最終由Sink并行地批量寫入對應的目標表中往产。
為了避免累積的數(shù)據(jù)過多造成GC壓力被碗,slot超過一定時間后即使沒有累積到一個batch size也會生成Insert Split分發(fā)給Sink執(zhí)行寫入。此外還要考慮另外一個問題:當生產者生產過快導致消費者來不及處理時捂齐,將會導致事件堆積蛮放,嚴重時還會OOM,即所謂的背壓(Backpressure)奠宜。幸好RxJava作為一個成熟的Reactive框架已經(jīng)對背壓處理有很好的支持包颁,這也是為什么要基于RxJava來實現(xiàn)的重要原因之一。
3.2 增量同步
binlog的抽取使用了開源的Java類庫mysql-binlog-connector-java压真,與Canal相比更加輕量娩嚼,源碼清晰易懂,不依賴其他第三方jar包滴肿,也沒有那么多不需要的繁雜功能岳悟。
為了實現(xiàn)對binlog的字段轉換,采用了Apache開源的SQL引擎calcite來實現(xiàn):將binlog的每行數(shù)據(jù)根據(jù)原表的表結構映射為一張內存表泼差,然后由calcite執(zhí)行SQL轉換后輸出結果贵少。(PS:calcite當前已被多個開源項目采用,Hive用calcite優(yōu)化查詢堆缘,F(xiàn)link的Streaming SQL基于calcite實現(xiàn)滔灶,Kylin的查詢引擎也采用calcite)
3.3 數(shù)據(jù)校驗
因MySQL表的checksum與數(shù)據(jù)的行順序無關,當新表與舊表的表結構相同并且數(shù)據(jù)不需要轉換時采用執(zhí)行CHECKSUM TABLE tbl_name查詢語句獲取每張新表和舊表的checksum吼肥,然后分別求和對比最終的checksum是否相同以此校驗數(shù)據(jù)是否一致录平。
當新表與舊表存在字段類型變更麻车、字段數(shù)量不一致、數(shù)據(jù)經(jīng)過轉換等會導致checksum發(fā)生變化時斗这,采用排除有關字段动猬,由遷移工具內部只對剩余字段數(shù)據(jù)進行checksum計算。Checksum算法可以選擇CRC32或Adler32表箭,這兩種算法均采用Java自帶的實現(xiàn)類赁咙,默認情況下使用Adler32因為其具有更快的計算效率。
四燃逻、總結
無論是分庫分表常規(guī)方案的實施序目,還是未來新一代分布式關系型數(shù)據(jù)存儲NewSQL的落地實踐,數(shù)據(jù)的遷移與同步都是必不可少的重要環(huán)節(jié)伯襟。畢竟猿涨,快速、準確姆怪、平滑地完成數(shù)據(jù)遷移叛赚,便已成功了一半。