数据迁移,不停机上线的正确姿势
互聯(lián)網(wǎng)系統(tǒng),經(jīng)常會有數(shù)據(jù)遷移的需求。系統(tǒng)從機房遷移到云平臺,從一個云平臺遷移到另一個云平臺,系統(tǒng)重構(gòu)后表結(jié)構(gòu)發(fā)生了變化,分庫分表,更換數(shù)據(jù)庫選型等等,很多場景都需要遷移數(shù)據(jù)。
在互聯(lián)網(wǎng)行業(yè),很多系統(tǒng)的訪問量很高,即便在凌晨兩三點也有一定的訪問量。由于系統(tǒng)數(shù)據(jù)遷移,導(dǎo)致服務(wù)暫停幾分鐘,是很難被業(yè)務(wù)方接受的!本文我們就來聊一下,在用戶無感知的前提下,如何設(shè)計不停機數(shù)據(jù)遷移方案!
數(shù)據(jù)遷移過程我們要注意哪些關(guān)鍵點呢?第一,保證遷移后數(shù)據(jù)準確不丟失,即每條記錄準確而且不丟失記錄;第二,不影響用戶體驗(尤其是訪問量高的C端業(yè)務(wù)需要不停機平滑遷移);第三,保證遷移后的性能和穩(wěn)定性。
數(shù)據(jù)遷移方案
掛從庫
在主庫上建一個從庫。從庫數(shù)據(jù)同步完成后,將從庫升級成主庫(新庫),再將流量切到新庫。
這種方式適合數(shù)據(jù)結(jié)構(gòu)不變,而且空閑時間段流量很低,允許停機遷移的場景。一般發(fā)生在平臺遷移的場景,如從機房遷移到云平臺,從一個云平臺遷移到另一個云平臺。大部分中小型互聯(lián)網(wǎng)系統(tǒng),空閑時段訪問量很低。在空閑時段,幾分鐘的停機時間,對用戶影響很小,業(yè)務(wù)方是可以接受的。所以我們可以采用停機遷移的方案。步驟如下:
1,新建從庫(新數(shù)據(jù)庫),數(shù)據(jù)開始從主庫向從庫同步。
2,數(shù)據(jù)同步完成后,找一個空閑時間段。為了保證主從數(shù)據(jù)庫數(shù)據(jù)一致,需要先停掉服務(wù),然后再把從庫升級為主庫。如果訪問數(shù)據(jù)庫用的是域名,直接解析域名到新數(shù)據(jù)庫(從庫升級成的主庫),如果訪問數(shù)據(jù)庫用的是IP,將IP改成新數(shù)據(jù)庫IP。
3,最后啟動服務(wù),整個遷移過程完成。
這種遷移方案的優(yōu)勢是遷移成本低,遷移周期短。缺點是,切換數(shù)據(jù)庫過程需要停止服務(wù)。
雙寫
老庫和新庫同時寫入,然后將老數(shù)據(jù)批量遷移到新庫,最后流量切換到新庫并關(guān)閉老庫讀寫。
這種方式適合數(shù)據(jù)結(jié)構(gòu)發(fā)生變化,不允許停機遷移的場景。一般發(fā)生在系統(tǒng)重構(gòu)時,數(shù)據(jù)結(jié)構(gòu)會發(fā)生變化,如表結(jié)構(gòu)改變或者分庫分表等場景。有些大型互聯(lián)網(wǎng)系統(tǒng),平常并發(fā)量很高,即便是空閑時段也有相當?shù)脑L問量。幾分鐘的停機時間,對用戶也會有明顯的影響,甚至導(dǎo)致一定的用戶流失,這對業(yè)務(wù)方來說是無法接受的。所以我們需要考慮一種用戶無感知的不停機遷移方案。
以筆者之前經(jīng)歷的用戶系統(tǒng)重構(gòu)為例,聊一下具體方案。當時的場景是這樣的,用戶表記錄數(shù)達到3000萬時,系統(tǒng)性能和可維護性變差,于是我們將用戶中心從單體工程中拆分出來并做了重構(gòu),重新設(shè)計了表結(jié)構(gòu),而且業(yè)務(wù)方要求不停機上線!下面是我們當時的方案,步驟如下:
代碼準備。在服務(wù)層對用戶表進行增刪改的地方,要同時操作新庫和老庫,需要修改相應(yīng)的代碼(同時寫新庫和老庫)。準備遷移程序腳本,用于做老數(shù)據(jù)遷移。準備校驗程序腳本,用于校驗新庫和老庫的數(shù)據(jù)是否一致。
開啟雙寫,老庫和新庫同時寫入。注意:任何對數(shù)據(jù)庫的增刪改都要雙寫;對于更新操作,如果新庫沒有相關(guān)記錄,需要先從老庫查出記錄,將更新后的記錄寫入新庫;為了保證寫入性能,老庫寫完后,可以采用消息隊列異步寫入新庫。
利用腳本程序,將某一時間戳之前的老數(shù)據(jù)遷移到新庫。注意:1,時間戳一定要選擇開啟雙寫后的時間點(比如開啟雙寫后10分鐘的時間點),避免部分老數(shù)據(jù)被漏掉;2,遷移過程遇到記錄沖突直接忽略(因為第2步的更新操作,可能已經(jīng)把記錄拉到了新庫);3,遷移過程一定要記錄日志,尤其是錯誤日志,如果有雙寫失敗的情況,我們可以通過日志恢復(fù)數(shù)據(jù),以此來保證新老庫的數(shù)據(jù)一致。
第3步完成后,我們還需要通過腳本程序檢驗數(shù)據(jù),看新庫數(shù)據(jù)是否準確以及有沒有漏掉的數(shù)據(jù)
數(shù)據(jù)校驗沒問題后,開啟雙讀,起初給新庫放少部分流量,新庫和老庫同時讀取。由于延時問題,新庫和老庫可能會有少量數(shù)據(jù)記錄不一致的情況,所以新庫讀不到時需要再讀一遍老庫。逐步將讀流量切到新庫,相當于灰度上線的過程。遇到問題可以及時把流量切回老庫
讀流量全部切到新庫后,關(guān)閉老庫寫入(可以在代碼里加上熱配置開關(guān)),只寫新庫
遷移完成,后續(xù)可以去掉雙寫雙讀相關(guān)無用代碼。
利用數(shù)據(jù)同步工具
????我們可以看到上面雙寫的方案比較麻煩,很多數(shù)據(jù)庫寫入的地方都需要修改代碼。有沒有更好的方案呢?
????我們還可以利用Canal,DataBus等工具做數(shù)據(jù)同步。以阿里開源的Canal為例。
上面是Canal的原理圖,
1,Canal模擬mysql slave的交互協(xié)議,把自己偽裝成mysql的從庫
2,向mysql master發(fā)送dump協(xié)議
3. mysql master收到dump協(xié)議,發(fā)送binary log給slave(canal)
4. canal解析binary log字節(jié)流對象,根據(jù)應(yīng)用場景對binary log字節(jié)流做相應(yīng)的處理
所以上面的用戶系統(tǒng)數(shù)據(jù)遷移,就不需要開啟雙寫了,服務(wù)層也不需要編寫雙寫的代碼,直接用Canal做增量數(shù)據(jù)同步即可。相應(yīng)的步驟就變成了:
代碼準備。準備Canal代碼,解析binary log字節(jié)流對象,并把解析好的用戶數(shù)據(jù)寫入新庫。準備遷移程序腳本,用于做老數(shù)據(jù)遷移。準備校驗程序腳本,用于校驗新庫和老庫的數(shù)據(jù)是否一致。
運行Canal代碼,開始增量數(shù)據(jù)(線上產(chǎn)生的新數(shù)據(jù))從老庫到新庫的同步。
利用腳本程序,將某一時間戳之前的老數(shù)據(jù)遷移到新庫。注意:1,時間戳一定要選擇開始運行Canal程序后的時間點(比如運行Canal代碼后10分鐘的時間點),避免部分老數(shù)據(jù)被漏掉;3,遷移過程一定要記錄日志,尤其是錯誤日志,如果有些記錄寫入失敗,我們可以通過日志恢復(fù)數(shù)據(jù),以此來保證新老庫的數(shù)據(jù)一致。
第3步完成后,我們還需要通過腳本程序檢驗數(shù)據(jù),看新庫數(shù)據(jù)是否準確以及有沒有漏掉的數(shù)據(jù)
數(shù)據(jù)校驗沒問題后,開啟雙讀,起初給新庫放少部分流量,新庫和老庫同時讀取。由于延時問題,新庫和老庫可能會有少量數(shù)據(jù)記錄不一致的情況,所以新庫讀不到時需要再讀一遍老庫。逐步將讀流量切到新庫,相當于灰度上線的過程。遇到問題可以及時把流量切回老庫
讀流量全部切到新庫后,將寫入流量切到新庫(可以在代碼里加上熱配置開關(guān)。注:由于切換過程Canal程序還在運行,仍然能夠獲取老庫的數(shù)據(jù)變化并同步到新庫,所以切換過程不會導(dǎo)致部分老庫數(shù)據(jù)無法同步新庫的情況)
關(guān)閉Canal程序
遷移完成。
此外,對于數(shù)據(jù)結(jié)構(gòu)不改變的不停機數(shù)據(jù)遷移,也可以利用Canal處理。除了第3步DBA可以直接利用工具做老數(shù)據(jù)的遷移,其他步驟基本和上面一樣。
希望本文對大家有所幫助。原創(chuàng)不易,如果感覺本文有幫助,有勞轉(zhuǎn)發(fā)或點一下“在看”!讓更多人收獲知識!
作者簡介:曾任職于阿里巴巴,每日優(yōu)鮮等互聯(lián)網(wǎng)公司,任技術(shù)總監(jiān),15年電商互聯(lián)網(wǎng)經(jīng)歷,擅長高并發(fā)場景下系統(tǒng)性能和穩(wěn)定性解決方案。
IT技術(shù)分享社區(qū)
個人博客網(wǎng)站:https://programmerblog.xyz
文章推薦程序員效率:畫流程圖常用的工具程序員效率:整理常用的在線筆記軟件遠程辦公:常用的遠程協(xié)助軟件,你都知道嗎?51單片機程序下載、ISP及串口基礎(chǔ)知識硬件:斷路器、接觸器、繼電器基礎(chǔ)知識
總結(jié)
以上是生活随笔為你收集整理的数据迁移,不停机上线的正确姿势的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jersey创建restful服务及调用
- 下一篇: php怎么查询数据库密码,如何验证php