首頁  >  文章  >  Java  >  兆級資料應該遷移的方法

兆級資料應該遷移的方法

coldplay.xixi
coldplay.xixi轉載
2020-10-23 17:20:412437瀏覽

java基礎教學專欄介紹萬億資料應該遷移的方法。

兆級資料應該遷移的方法

背景

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

  • 分庫分錶:業務發展越來越快,導致單機資料庫承受的壓力越來越大,資料量也越來越多,這個時候通常會使用分庫的方法去解決這個問題,將資料庫的流量均分到不同的機器上。從單機資料庫到分庫這個過程,我們需要完整的遷移我們的數據,我們才能成功的分庫的方式上使用我們的數據。
  • 更換儲存媒體:上面介紹的分庫,一般來說我們遷移完之後,儲存媒體仍然是同樣的,比如說之前使用的是單機Mysql,分庫之後就變成了多台機器的Mysql,我們的資料庫表的欄位都沒有改變,遷移來說相對比較簡單。有時候我們分庫分錶並不能解決所有的問題,如果我們需要很多複雜的查詢,這個時候使用Mysql可能就不是一個可靠的方案,那麼我們就需要替換查詢的儲存介質,例如使用elasticsearch,這種的遷移就會稍微複雜一些,牽涉到不同儲存媒體的資料轉換。
  • 切換新系統:一般公司在高速發展中,一定會出現很多為了速度快然後重複建設的項目,當公司再一定時間段的時候,往往這部分項目會被合併,變成一個平台或中台,像我們一些會員系統,電商系統等等。這時候往往就會面臨一個問題,將老的系統中的資料需要遷移到新的系統中,這個時候就更加複雜了,有可能不僅是儲存媒體有變動,有可能專案語言也不同,從更上層的角度來看,部門有可能也不同,所以這種資料遷移的難度是比較高,風險也更加的大。

在實際業務開發中,我們會根據不同的情況來做不同的遷移方案,接下來我們來討論一下到底應該怎麼遷移資料。

資料遷移

資料遷移其實不是一蹴而就的,每一次資料遷移都需要一段漫長的時間,有可能是一周,有可能是幾個月,通常來說我們遷移數據的過程基本上都跟下圖差不多:兆級資料應該遷移的方法' 首先我們需要將我們資料庫已經存在的數據進行批量的遷移,然後需要處理新增的這部分數據,需要實時的把這部分數據在寫完原本的數據庫之後然後寫到我們的新的存儲,在這一過程中我們需要不斷的進行資料校驗。當我們校驗基本問題不大的時候,然後進行切流操作,直到完全切流之後,我們就可以不用再進行資料校驗和增量資料遷移。

存量資料遷移

首先我們來說一下存量資料遷移應該怎麼做,存量資料遷移在開源社群中搜尋了一圈發現沒有太好用的工具,目前來說阿里雲端的DTS提供了存量資料遷移,DTS支援同構和異質不同資料來源之間的遷移,基本上支援業界常見的資料庫例如Mysql,Orcale,SQL Server等等。 DTS比較適合我們之前說的前兩個場景,一個是分庫的場景,如果使用的是阿里雲的DRDS那麼就可以直接將資料通過DTS遷移到DRDS,另外一個是資料異構的場景,無論是Redis還是ES,DTS都支援直接進行遷移。

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

  1. 當存量遷移任務啟動的時候,我們取得目前需要遷移的最大的id和最小id
  2. ##設定一個分段,例如1萬,從最小id開始每次查詢1萬的資料給DTS伺服器,交給DTS處理。 sql如下:
  3. select * from table_name where id > curId and id < curId + 10000;复制代码
3.當id大於等於maxId之後,存量資料遷移任務結束

當然我們在實際的遷移過程中可能不會去使用阿里雲,或者說在我們的第三個場景下,我們的資料庫字段之間需要做很多轉換,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:

相關免費學習推薦:java基礎教學

#

以上是兆級資料應該遷移的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.im。如有侵權,請聯絡admin@php.cn刪除