您好,登錄后才能下訂單哦!
本篇內容主要講解“分庫分表擴容怎么實現平滑數據遷移”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“分庫分表擴容怎么實現平滑數據遷移”吧!
2020年, 筆者負責的一個高德打車彈外訂單系統進行了一次擴分庫分表和數據庫遷移。 該訂單系統整體部署在阿里云上,服務使用阿里云ECS部署,數據庫采用阿里云RDS,配置中心基于阿里云ACM自 研,數 據同步基于阿里云DTS自研以及自研分庫分表組件、分布式ID組件等等。
此次進行擴分庫分表的背景是,原4實例4庫、每個庫64張表一共256張表,部分單表已超千萬量級,按當前每日單量量級,一年內單表會達到上億條記錄,單表數據量過大會帶來數據庫性能問題。
注 : 【彈內彈外】彈是指彈性計算,彈內與彈外其實是指兩套獨立的彈性計算網絡環境。 彈內主要是指部署在阿里生產網的彈性計算環境,最早是基于原有淘寶技術構建的,主要用于支撐淘寶業務。 彈外主要是指部署在阿里公有云的彈性計算環境,支撐了阿里云計算業務。
1.當前分庫分表情況
4實例(16C/64G/3T SSD),4庫(每個實例一個庫),每庫64張表,共256張表。
通過RDS后臺一鍵診斷功能,來計算表空間使用情況(這里 拿測試環境數據庫舉例) 。
2.容量計算
實例數
數據庫的瓶頸主要體現在:磁盤、CPU、內存、網絡、連接數,而連接數主要是受 CPU 和內存影響。 CPU 和內存可以通過動態升配來提升,但是SSD磁盤容量最大支持到6T(32C以下最大3T、32C及以上最大6T)。
但是現階段兼顧成本,可先將實例擴容一倍,采用8個實例(16C/64G/3T SSD),每個實例建4個庫(database)、每個庫128張表(這里實際上是一個成本取舍的過程,理論上應該采取"多庫少表"的原則,單庫128張表其實太多了,單庫建議32或64張表為宜) 。
后續如果實例壓力提升可進行實例配置升級(16C/128G、32C/128G、32C/256G等);未來如出現單實例升配無法解決,在考慮擴容實例,只需要將database遷移至新實例,遷移成本較小。
表數
按單表最多1000w條數據評估,4096張表可支持日5000w單*3年(10.1壓測標準)、日2000w單*5年的架構。(因業務表比較多,此處忽略掉單條數據大小的計算過程)
庫數
32個庫,每個庫128張表。未來可最大擴容到32個實例,無需rehash,只需要遷移數據。
阿里云RDS規格和價格一覽
因擴分庫分表涉及到rehash過程(256表變4096表),而阿里云DTS只支持同構庫數據遷移,所以我們基于DTS的binlog轉kafka能力自研了數據同步中間件。
整個數據遷移工作包括:前期準備、數據同步環節(歷史數據全量同步、增量數據實時同步、rehash)、數據校驗環節(全量校驗、實時校驗、校驗規則配置)、數據修復工具等。
1.準備工作
唯一業務ID
在進行數據同步前,需要先梳理所有表的唯一業務ID,只有確定了唯一業務ID才能實現數據的同步操作。
需要注意的是:
業務中是否有使用數據庫自增ID做為業務ID使用的,如果有需要業務先進行改造,還好訂單業務里沒有。
每個表是否都有唯一索引,這個在梳理的過程中發現有幾張表沒有唯一索引。
一旦表中沒有唯一索引,就會在數據同步過程中造成數據重復的風險,所以我們先將沒有唯一索引的表根據業務場景增加唯一索引(有可能是聯合唯一索引)。
在這里順便提一下,阿里云DTS做同構數據遷移,使用的是數據庫自增ID做為唯一ID使用的,這種情況如果做雙向同步,會造成數據覆蓋的問題。解決方案也有,之前我們的做法是,新舊實體采用自增ID單雙號解決,保證新舊實例的自增ID不會出現沖突就行。因為這次我們使用的自研雙向同步組件,這個問題這里不細聊。
分表規則梳理
分表規則不同決定著rehash和數據校驗的不同。需逐個表梳理是用戶ID緯度分表還是非用戶ID緯度分表、是否只分庫不分表、是否不分庫不分表等等。
2.數據同步
數據同步整體方案見下圖,數據同步基于binlog,獨立的中間服務做同步,對業務代碼無侵入。
接下來對每一個環節進行介紹。
歷史數據全量同步
單獨一個服務,使用游標的方式從舊庫分批select數據,經過rehash后批量插入(batch insert)到新庫,此處需要配置jdbc連接串參數rewriteBatchedStatements=true才能使批處理操作生效。
另外特別需要注意的是,歷史數據也會存在不斷的更新,如果先開啟歷史數據全量同步,則剛同步完成的數據有可能不是最新的。所以這里的做法是,先開啟增量數據單向同步(從舊庫到新庫),此時只是開啟積壓kafka消息并不會真正消費;然后在開始歷史數據全量同步,當歷史全量數據同步完成后,在開啟消費kafka消息進行增量數據同步(提高全量同步效率減少積壓也是關鍵的一環),這樣來保證遷移數據過程中的數據一致。
增量數據實時同步
增量數據同步考慮到灰度切流穩定性、容災和可回滾能力,采用實時雙向同步方案,切流過程中一旦新庫出現穩定性問題或者新庫出現數據一致問題,可快速回滾切回舊庫,保證數據庫的穩定和數據可靠。
增量數據實時同步采用基于阿里云DTS的數據訂閱自研數據同步組件data-sync實現,主要方案是DTS數據訂閱能力會自動將被訂閱的數據庫binlog轉為kafka,data-sync組件訂閱kafka消息、將消息進行過濾、合并、分組、rehash、拆表、批量insert/update,最后再提交offset等一系列操作,最終完成數據同步工作。
過濾循環消息:需要過濾掉循環同步的binlog消息,這個問題比較重要后面將進行單獨介紹。
數據合并:同一條記錄的多條操作只保留最后一條。為了提高性能,data-sync組件接到kafka消息后不會立刻進行數據流轉,而是先存到本地阻塞隊列,然后由本地定時任務每X秒將本地隊列中的N條數據進行數據流轉操作。此時N條數據有可能是對同一張表同一條記錄的操作,所以此處只需要保留最后一條(類似于redis aof重寫) 。
update轉insert:數據合并時,如果數據中有insert+update只保留最后一條update,會執行失敗,所以此處需要將update轉為insert語句。
按新表合并:將最終要提交的N條數據,按照新表進行拆分合并,這樣可以直接按照新表緯度進行數據庫批量操作,提高插入效率。
整個過程中有幾個問題需要注意:
問題1:怎么防止因異步消息無順序而導致的數據一致問題?
首先kafka異步消息是存在順序問題的,但是要知道的是binlog是順序的,所以dts在對詳細進行kafka消息投遞時也是順序的,此處要做的就是一個庫保證只有一個消費者就能保障數據的順序問題、不會出現數據狀態覆蓋,從而解決數據一致問題。
問題2:是否會有丟消息問題,比如消費者服務重啟等情況下?
這里沒有采用自動提交offset,而是每次消費數據最終入庫完成后,將offset異步存到一個mysql表中,如果消費者服務重啟宕機等,重啟后從mysql拿到最新的offset開始消費。這樣唯一的一個問題可能會出現瞬間部分消息重復消費,但是因為上面介紹的binlog是順序的,所以能保證數據的最終一致。
問題3:update轉insert會不會丟字段?
binlog是全字段發送,不會存在丟字段情況。
問題4:循環消息問題。
后面進行單獨介紹。
rehash
前文有提到,因為是256表變4096表,所以數據每一條都需要經過一次rehash重新做分庫分表的計算。
要說rehash,就不得不先介紹下當前訂單數據的分庫分表策略,訂單ID中冗余了用戶ID的后四位,通過用戶ID后四位做hash計算確定庫號和表號。
數據同步過程中,從舊庫到新庫,需要拿到訂單ID中的用戶ID后四位模4096,確定數據在新庫中的庫表位置;從新庫到舊庫,則需要用用戶ID后四位模256,確定數據在舊庫中的庫表位置。
雙向同步時的binlog循環消費問題
想象一下,業務寫一條數據到舊實例的一張表,于是產生了一條binlog;data-sync中間件接到binlog后,將該記錄寫入到新實例,于是在新實例也產生了一條binlog;此時data-sync中間件又接到了該binlog......不斷循環,消息越來越多,數據順序也被打亂。
怎么解決該問題呢?我們采用數據染色方案,只要能夠標識寫入到數據庫中的數據使data-sync中間件寫入而非業務寫入,當下次接收到該binlog數據的時候就不需要進行再次消息流轉。
所以data-sync中間件要求,每個數據庫實例創建一個事務表,該事務表tb_transaction只有id、tablename、status、create_time、update_time幾個字段,status默認為0。
再回到上面的問題,業務寫一條數據到舊實例的一張表,于是產生了一條binlog;data-sync中間件接到binlog后,如下操作:
# 開啟事務,用事務保證一下sql的原子性和一致性 start transaction; set autocommit = 0; # 更新事務表status=1,標識后面的業務數據開始染色 update tb_transaction set status = 1 where tablename = ${tableName}; # 以下是業務產生binlog insert xxx; update xxx; update xxx; # 更新事務表status=0,標識后面的業務數據失去染色 update tb_transaction set status = 0 where tablename = ${tableName}; commit;
此時data-sync中間件將上面這些語句打包一起提交到新實例,新實例更新數據后也會生產對應上面語句的binlog;當data-sync中間件再次接收到binlog時,只要判斷遇到tb_transaction表status=1的數據開始,后面的數據都直接丟棄不要,直到遇到status=0時,再繼續接收數據,以此來保證data-sync中間件只會流轉業務產生的消息。
3.數據校驗
數據校驗模塊由數據校驗服務data-check模塊來實現,主要是基于數據庫層面的數據對比,逐條核對每一個數據字段是否一致,不一致的話會經過配置的校驗規則來進行重試或者報警。
全量校驗
以舊庫為基準,查詢每一條數據在新庫是否存在,以及個字段是否一致。
以新庫為基準,查詢每一條數據在舊庫是否存在,以及個字段是否一致。
實時校驗
定時任務每5分鐘校驗,查詢最近5+1分鐘舊庫和新庫更新的數據,做diff。
差異數據進行二次、三次校驗(由于并發和數據延遲存在),三次校驗都不同則報警。
4.數據修復
經過數據校驗,一旦發現數據不一致,則需要對數據進行修復操作。
數據修復有兩種方案,一種是適用于大范圍的數據不一致,采用重置kafka offset的方式,重新消費數據消息,將有問題的數據進行覆蓋。
第二種是適用于小范圍的數據不一致,數據修復模塊自動拉取數據校驗data-check模塊記錄的sls日志,進行日志解析,生成同步語句,更新到目標庫。
1.整體灰度切流方案
整體灰度方案:SP+用戶緯度來實現,SP緯度:依靠灰度環境切量來做,用戶緯度:依賴用戶ID后四位百分比切流。
灰度切量的過程一定要配合停寫(秒級),為什么要停寫,因為數據同步存在一定延遲(正常毫秒級),而所有業務操作一定要保障都在一個實例上,否則在舊庫中業務剛剛修改了一條數據,此時切換到新庫如果數據還沒有同步過來就是舊數據會有數據一致問題。所以步驟應該是:
先停寫
觀察數據全部同步完
在切換數據源
最后關閉停寫,開始正常業務寫入
2.切流前準備——ABC驗證
雖然在切流之前,在測試環境進過了大量的測試,但是測試環境畢竟和生產環境不一樣,生產環境數據庫一旦出問題就可能是滅頂之災,雖然上面介紹了數據校驗和數據修復流程,但是把問題攔截在發生之前是做服務穩定性最重要的工作。
因此我們提出了ABC驗證的概念,灰度環境ABC驗證準備:
新購買兩套數據庫實例,當前訂單庫為A,新買的兩套為分別為B、C
配置DTS從A單項同步到B(dts支持同構不需要rehash的數據同步),B做為舊庫的驗證庫,C庫做為新庫
用B和C做為生產演練驗證
當B和C演練完成之后,在將A和C配置為正式的雙向同步
3.灰度切流步驟
具體灰度方案和數據源切換流程:
代碼提前配置好兩套數據庫分庫分表規則。
通過ACM配置灰度比例。
代碼攔截mybatis請求,根據用戶id后四位取模,和ACM設置中設置的灰度比例比較,將新庫標識通過ThreadLocal傳遞到分庫分表組件。
判斷當前是否有灰度白名單,如命中將新庫標識通過ThreadLocal傳遞到分庫分表組件。
分庫分表組件根據ACM配置拿到新分庫的分表規則,進行數據庫讀寫操作。
切量時會配合ACM配置灰度比例命中的用戶進行停寫。
整個數據遷移過程還是比較復雜的,時間也不是很充裕(過程中還穿插著十一全鏈路壓測改造),在有限的時間內集大家之力重復討論挖掘可能存在的問題,然后論證解決方案,不放過任何一個可能出現問題的環節,還是那句話,把問題攔截在發生之前是做服務穩定性最重要的工作。
過程中的細節還是很多的,從數據遷移的準備工作到數據同步測試,從灰度流程確定到正式生產切換,尤其是結合業務和數據的點,有很多需要考慮的細節,文中沒有一一列出。
最終經過近兩個月的緊張工作,無業務代碼侵入、零事故、平穩地完成了擴分庫分表和數據遷移的工作。
到此,相信大家對“分庫分表擴容怎么實現平滑數據遷移”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。