萬億級數據應該怎麼遷移?

以前寫過一篇文章:binlog還能這麼用之Canal篇,在裏面介紹了一些增量數據遷移的一些方法,可是對於總體的數據的遷移介紹得不是很深,這裏想對遷移數據總體作一個介紹,但願能幫助到你們。redis

背景

在星爺的《大話西遊》中有一句很是出名的臺詞:「曾經有一份真摯的感情擺在個人面前我沒有珍惜,等我失去的時候才追悔莫及,人間最痛苦的事莫過於此,若是上天能給我一次再來一次的機會,我會對哪一個女孩說三個字:我愛你,若是非要在這份愛上加一個期限,我但願是一萬年!」在咱們開發人員的眼中,這個感情就和咱們數據庫中的數據同樣,咱們多但願他一萬年都不改變,可是每每事與願違,隨着公司的不斷髮展,業務的不斷變動,咱們對數據的要求也在不斷的變化,大概有下面的幾種狀況:算法

  • 分庫分表:業務發展愈來愈快,致使單機數據庫承受的壓力愈來愈大,數據量也愈來愈多,這個時候一般會使用分庫的方法去解決這個問題,將數據庫的流量均分到不一樣的機器上。從單機數據庫到分庫這個過程,咱們就須要完整的遷移咱們的數據,咱們才能成功的分庫的方式上使用咱們的數據。
  • 更換存儲介質:上面介紹的分庫,通常來講咱們遷移完以後,存儲介質依然是一樣的,好比說以前使用的是單機Mysql,分庫以後就變成了多臺機器的Mysql,咱們的數據庫表的字段都沒有發生變化,遷移來講相對比較簡單。有時候咱們分庫分表並不能解決全部的問題,若是咱們須要不少複雜的查詢,這個時候使用Mysql可能就不是一個靠譜的方案,那麼咱們就須要替換查詢的存儲介質,好比使用elasticsearch,這種的遷移就會稍微要複雜一些,涉及到不一樣存儲介質的數據轉換。
  • 切換新系統:通常公司在高速發展中,必定會出現不少爲了速度快而後重複建設的項目,當公司再必定時間段的時候,每每這部分項目會被合併,變成一個平臺或者中臺,好比咱們一些會員系統,電商系統等等。這個時候每每就會面臨一個問題,將老的系統中的數據須要遷移到新的系統中,這個時候就更加複雜了,有可能不只是存儲介質有變更,有可能項目語言也不一樣,從更上層的角度來看,部門有可能也不一樣,因此這種數據遷移的難度是比較高,風險也更加的大。

在實際業務開發中,咱們會根據不一樣的狀況來作出不一樣的遷移方案,接下來咱們來討論一下到底應該怎麼遷移數據。sql

數據遷移

數據遷移其實不是一蹴而就的,每一次數據遷移都須要一段漫長的時間,有多是一週,有多是幾個月,一般來講咱們遷移數據的過程基本都和下圖差很少: ‘ 首先咱們須要將咱們數據庫已經存在的數據進行批量的遷移,而後須要處理新增的這部分數據,須要實時的把這部分數據在寫完本來的數據庫以後而後寫到咱們的新的存儲,在這一過程當中咱們須要不斷的進行數據校驗。當咱們校驗基本問題不大的時候,而後進行切流操做,直到徹底切流以後,咱們就能夠不用再進行數據校驗和增量數據遷移。數據庫

存量數據遷移

首先咱們來講一下存量數據遷移應該怎麼作,存量數據遷移在開源社區中搜索了一圈發現沒有太好用的工具,目前來講阿里雲的DTS提供了存量數據遷移,DTS支持同構和異構不一樣數據源之間的遷移,基本支持業界常見的數據庫好比Mysql,Orcale,SQL Server等等。DTS比較適合咱們以前說的前兩個場景,一個是分庫的場景,若是使用的是阿里雲的DRDS那麼就能夠直接將數據經過DTS遷移到DRDS,另一個是數據異構的場景,不管是Redis仍是ES,DTS都支持直接進行遷移。服務器

那麼DTS的存量遷移怎麼作的呢?其實比較簡單大概就是下面幾個步驟:異步

  1. 當存量遷移任務啓動的時候,咱們獲取當前須要遷移的最大的id和最小id
  2. 設置一個分段,好比1萬,從最小id開始每次查詢1萬的數據給DTS服務器,交給DTS處理。sql以下:
select * from table_name where id > curId and id < curId + 10000;

3.當id大於等於maxId以後,存量數據遷移任務結束elasticsearch

固然咱們在實際的遷移過程當中可能不會去使用阿里雲,或者說在咱們的第三個場景下,咱們的數據庫字段之間須要作不少轉換,DTS不支持,那麼咱們就能夠模仿DTS的作法,經過分段批量讀取數據的方式來遷移數據,這裏須要注意的是咱們批量遷移數據的時候須要控制分段的大小,以及頻率,防止影響咱們線上的正常運行。分佈式

增量數據遷移

存量數據的遷移方案比較有限,可是增量的數據遷移方法就是百花齊放了,通常來講咱們有下面的幾種方法:工具

  • DTS: 阿里雲的DTS算是一條龍服務了,在提供存量數據遷移的同時也提供了增量數據遷移,只不過須要按量收費。
  • 服務雙寫:比較適合於系統沒有切換的遷移,也就是隻換了存儲可是系統仍是同一個,好比說分庫分表,redis數據同步等,這個的作法比較簡單直接在代碼裏面同步的去寫入須要遷移的數據,可是因爲不是同一個數據庫就不能保證事務,有可能致使遷移數據的時候會出現數據丟失,這個過程經過後續的數據校驗會進行解決。
  • MQ異步寫入:這個能夠適用於全部的場景,當有數據修改的時候發送一個MQ消息,消費者收到這個消息以後再進行數據更新。這個和上面的雙寫有點相似,可是他把數據庫的操做變成了MQ異步了出問題的機率就會小不少
  • 監聽binlog: 咱們可使用以前說過的canal或者其餘的一些開源的如databus去進行binlog監聽,監聽binlog的方式 就和上面的消息MQ方式同樣,只是發送消息的這一步被咱們省略了。這個方式的一個開發量來講基本是最小的。

這麼多種方式咱們應該使用哪一種呢?我我的來講是比較推薦監聽binlog的作法的,監聽binlog減小開發成本,咱們只須要實現consumer邏輯便可,數據能保證一致性,由於是監聽的binlog這裏不須要擔憂以前雙寫的時候不是一個事務的問題。性能

數據校驗

前面所說的全部方案,雖然有不少是成熟的雲服務(dts)或者中間件(canal),可是他們都有可能出現一些數據丟失,出現數據丟失的狀況總體來講仍是比較少,可是很是難排查,有多是dts或者canal不當心抖了一下,又或者是接收數據的時候不當心致使的丟失。既然咱們沒有辦法避免咱們的數據在遷移的過程當中丟失,那麼咱們應該經過其餘手段來進行校訂。

一般來講咱們遷移數據的時候都會有數據校驗這一個步驟,可是在不一樣團隊可能會選取不一樣的數據校驗方案:

  • 以前在美團的時候,咱們會作一個雙讀,也就是咱們全部的讀取都會重新的裏面讀取一份,可是返回的仍是老的,這個時候咱們須要作這部分數據的校驗,若是有問題能夠發出報警人工修復或者自動修復。經過這種方式,咱們經常使用的數據就能很快的進行一個修復,固然也會不定時的去跑一個全量的數據check,只是這種check出來修復數據的時間就比較滯後。
  • 如今在猿輔導以後,咱們沒有采用以前的那種方式,由於雙讀check雖然能很快發現數據的不對,可是咱們並無對這部分數據有那麼高的一個實時性校驗而且雙讀的一個代碼開發量仍是稍微比較大的,可是又不能依靠不定時全量check去保證,這樣就會致使咱們的數據校驗時間會很是的延長。咱們採起了一個折中的方法,咱們借鑑了對帳裏面的T+1的一個思路,咱們天天凌晨獲取老數據庫中昨天更新的數據,而後和咱們新數據庫中的數據作一一比對,若是有數據不同或者數據缺失,咱們均可以立馬進行一個修復。

固然在實際開發過程當中咱們也須要注意下面幾點:

  • 數據校驗任務的一個正確性如何保證,校驗任務原本就是去校訂其餘數據的,可是若是他自身出現了問題,就失去了校驗的意義,這裏目前來講只能靠review代碼這種方式去保證校驗任務的正確性。
  • 校驗任務的時候須要注意日誌的打印,有時候出現問題多是直接全部數據出現問題,那麼校驗任務就有可能會打出大量的錯誤日誌,而後進行報警,有可能會將系統打掛,或者說影響其餘人的服務。這裏若是要簡單一點搞,能夠將一些非人工處理的報警搞成warn,複雜一點搞得話,能夠封裝一個工具,某個error打印再某個時間段超過必定量而後就不用再打印了。
  • 校驗任務注意不要影響線上運行的服務,一般校驗任務會寫不少批查詢的語句,會出現批量掃表的狀況,若是代碼沒有寫好很容易致使數據庫掛掉。

切流

當咱們數據校驗基本沒有報錯了以後,說明咱們的遷移程序是比較穩定的了,那麼咱們就能夠直接使用咱們新的數據了嗎?固然是不能夠的,若是咱們一把切換了,順利的話固然是很好的,若是出現問題了,那麼就會影響全部的用戶。

因此咱們接下來就須要進行灰度,也就是切流。 對於不一樣的業務切流的的維度會不同,對於用戶維度的切流,咱們一般會以userId的取模的方式去進行切流,對於租戶或者商家維度的業務,就須要按照租戶id取模的方式去切流。這個切流須要制定好一個切流計劃,在什麼時間段,放出多少的流量,而且切流的時候必定要選擇流量比較少的時候進行切流,每一次切流都須要對日誌作詳細的觀察,出現問題儘早修復,流量的一個放出過程是一個由慢到快的過程,好比最開始是以1%的量去不斷疊加的,到後面的時候咱們直接以10%,20%的量去快速放量。由於若是出現問題的話每每在小流量的時候就會發現,若是小流量沒有問題那麼後續就能夠快速放量。

注意主鍵ID

在遷移數據的過程當中特別要注意的是主鍵ID,在上面雙寫的方案中也提到過主鍵ID須要雙寫的時候手動的去指定,防止ID生成順序錯誤。

若是咱們是由於分庫分表而進行遷移,就須要考慮咱們之後的主鍵Id就不能是自增id,須要使用分佈式id,這裏比較推薦的是美團開源的leaf,他支持兩種模式一種是雪花算法趨勢遞增,可是全部的id都是Long型,適合於一些支持Long爲id的應用。還有一種是號段模式,這種會根據你設置的一個基礎id,從這個上面不斷的增長。而且基本都走的是內存生成,性能也是很是的快。

固然咱們還有種狀況是咱們須要遷移系統,以前系統的主鍵id在新系統中已經有了,那麼咱們的id就須要作一些映射。若是咱們在遷移系統的時候已經知道將來大概有哪些系統會遷移進來,咱們就能夠採用預留的方式,好比A系統如今的數據是1到1億,B系統的數據也是1到1億,咱們如今須要將A,B兩個系統合併成新系統,那麼咱們能夠稍微預估一些Buffer,好比給A系統留1到1.5億,這樣A就不須要進行映射,B系統是1.5億到3億,那麼咱們轉換成老系統Id的時候就須要減去1.5億,最後咱們新系統的新的Id就從3億開始遞增。 可是若是系統中沒有作規劃的預留段怎麼辦呢?能夠經過下面兩種方式:

  • 須要新增一個表,將老系統的id和新系統的id作一個映射記錄,這個工做量仍是比較大的,由於咱們通常遷移都會涉及幾十上百張表,記錄的成本仍是很是的高。
  • 若是id是Long型的話,咱們能夠好好利用long是64位這個因素,咱們能夠制定一個規則,咱們新系統的id都是從一個比較大的數開始,好比從大於Int的數開始,將小Int的那部分數均可以留給咱們的老系統作Id遷移,好比咱們上面的1.5億的數據量,其實只用了28位,咱們的Int是32位,那麼還有4位可使用,這個4位能夠表明16個系統作遷移,固然若是規劃中有更多的系統作遷移,能夠將新系統的id起始點設置得更大一點。以下圖所示:

總結

最後簡單來總結下這個套路,其實就是四個步驟,一個注意:存量,增量,校驗,切流,最後再注意一下id。無論是多大量級的數據,基本上按照這個套路來遷移就不會出現大的問題。但願能在你們的後續遷移數據工做中,這篇文章能幫助到你。

若是你們以爲這篇文章對你有幫助,你的關注和轉發是對我最大的支持,O(∩_∩)O:

相關文章
相關標籤/搜索