舊數據與新系統——重現數據遷移之旅
作者 | 李佳俊、程博
某天,正跟小張pair做卡呢。突然收到客戶通知:不用做了,這系統不要了。
啊? 都搞了好幾年,說不要就不要了,客戶這么財大氣粗?
細問之下,原來變化來源于最新的商業決策——客戶收購了一個市場占有率比較大的新公司,打算直接使用新公司已有的系統。那我們是不是可以下項目休息了?曹老大瞟了一眼我:你想得美。客戶希望把現有的生產數據遷出到新系統去。不是吧,又遷,咱們不是剛把之前收購的老系統數據遷到新開發的系統嗎?
模型分析
前方目標很明確:數據遷移,讓數據的價值在新系統中繼續發光發熱。
新老系統由于只是同一個領域的不同實現,所以模型上很相似。主要的源數據模型都能映射到目標模型,模型上的部分字段甚至也是等價的概念。但是畢竟是兩波開發實現的兩個不同的系統,很多業務概念叫法完全不同,首先就需要坐下來跟客戶一起統一模型語言。另外,還有很多新系統的目標字段不能由源數據直接獲得,有的字段甚至無法獲得。所以,我們根據不同類型的數據特征,將數據分成了4種類型:
- 直接映射,源數據模型的字段值直接映射為目標數據模型的字段值。
- 間接映射,源數據模型的一個或多個字段值經過一定的業務規則轉換為目標數據模型的字段值。
- 默認值,源數據模型沒有相關的字段,而目標數據模型又是必填的字段值。
- 丟棄,源數據模型沒有相關的字段,而目標數據模型也不是必填的字段值。
根據這四類規則,在和客戶友(lai)好(hui)討(la)論(che)了多輪之后,我們得到了一個數據映射表,將所有需要遷移的源數據模型映射為目標數據模型。
遷移策略
數據清楚了就開搞吧!等等,客戶對遷移又提了幾個新需求:
- 新系統的數據加載部分想復用已經寫好的腳本,腳本的輸入是json文件,所以希望使用json文件作為介質將老系統的數據傳輸到新系統。
- 在數據還沒全部遷到新系統之前,兩個系統同時都要用,因為客戶需要在用戶慢慢熟悉新系統的同時,使用老系統保證業務連貫性。
這意味著兩件事:老系統在幾個月內還會源源不斷的產生新數據;新系統需要在一定的時間段內看到老系統新產生的數據。這樣的狀況可能要持續很久,直到客戶喊停才能停。于是,遷移可能會變成這樣。
根據數據量的不同,我們把遷移又分成了全量遷移和增量遷移。
全量遷移關注的是如何把大批的數據一次性遷入到新系統,由于數據量較大,文件需要進行分片處理。增量數據則關注的是如何保證數據實時性,在客戶能夠容忍的時間范圍內,將增量數據同步到新系統。客戶對實時性要求并不高(一天之內),而且也不希望在新系統上新增API去做實時同步,所以定時任務已經能夠滿足增量數據同步的要求。
數據實體間存在單向依賴,希望按照依賴關系分批進行遷移,簡化的說明圖如下:
對于增量數據,也需要考慮根據依賴關系,調整定時任務的實體同步順序。
實現
其實整個遷移過程看起來就是一個經典的ETL過程,先將源數據從老系統提取(E)出來,根據數據映射表進行轉換(T),最后通過生成文件并加載(L)到新系統中。但在實現過程中還是遇到了不少麻煩。
1.AWS to GCP
這是我們在設計階段遇到的第一個大麻煩,老系統運行在AWS環境,而新系統運行在GCP環境。云遷移的案例在網上搜集相關資料比較少。Spike了幾輪之后,處于安全性,效率等因素考慮,我們決定使用GCP的Workload identity federation(WIF)進行云平臺間數據遷移。WIF是GCP提供的外部服務訪問GCP資源的服務,它旨在消除與服務帳號密鑰相關的維護和安全負擔。WIF遵循OAuth 2.0 令牌交換規范,令牌交換的主要過程大致為:
- 獲取AWS GetCallerIdentity token
- 使用AWS GetCallerIdentity token交換GCP federated access token
- 使用GCP federated access token交換GCP service account access token
- 使用GCP service account access token訪問GCP資源
在我們的項目場景中,由AWS lambda與GCP Security Token Service及Cloud IAM交互完成短期token的獲取,并持有短期token執行將S3中的文件存入Cloud storage的過程。
2.數據安全
數據本身全是跟錢相關的東西,文件又需要走公網從AWS傳輸到GCP,所以數據安全也是需要重點考慮的環節。雖然GCP本身已經提供了一定等級的數據加密,但是客戶還是不放心,希望我們在應用程序本身再提供一層數據安全的保障。于是根據數據安全經典過程,我們在AWS發送文件前對文件做了加密及簽名,并在GCP收到后文件完成解密及驗證。加密為了保證數據不被中間人竊取,簽名為了防止惡意傳輸攻擊文件:
3.數據合并
前文提過,由于歷史原因,老系統當前的數據其實也是從更老的系統中遷移過來了(更老的系統實際也還在使用中),老系統當前對外提供的API存在著大量的數據合并邏輯。如果以數據庫為源頭對涉及數據合并的實體進行遷移,那么就需要大量重寫這部分業務邏輯,而這將會是個漫長而又復雜的過程。如果直接使用當前系統的API,由于遷移訪問的數據量很大,又會對線上業務造成影響。
于是我們采取了一個折中的辦法,在生產環境中復制了一個只讀實例來規避對數據合并邏輯的重復工作,并且不影響線上環境的實例性能。
4.增量數據統計
對于遷移期間的增量數據,我們采用事件驅動的方式,通過監聽源數據庫的新增或修改,并觸發lambda執行轉換過程,將轉換后的數據存入中間表并記錄修改時間。在每天生成增量數據文件時,通過查詢所有比上次遷移時間晚的記錄,得到所有增量數據并最終生成本次文件。
5.數據校驗
在生成文件的過程中,我們會對實體的各個字段根據業務規則進行校驗,對不符合校驗規則的數據將其原始數據及錯誤原因收集到錯誤日志表中,方便我們后續修數據的環節。我們的測試環境雖然有一部分數據也來自生產環境,但出于安全原因,大部分字段都做過混淆。所以在測試環境無法收集到所有真實數據的錯誤,很難保證遷移的最終結果。
于是在上線前的一兩個迭代內我們做的事情是,在生產環境中進行若干輪預遷移(但不執行加載)。這樣每一輪預遷移會收集到一部分數據錯誤。這些數據錯誤大部分需要與客戶討論后進行處理,主要處理方式有:
- 數據本身輸入不合法,需要客戶確定正確的值并修復
- 數據本身輸入合法,根據新系統的表現形式,重新確定轉換邏輯給予合法值
- 數據無法修復且不重要,丟棄
這樣在經過多輪預遷移以后,我們修復了所有生產環境上的錯誤,從而保證了最終遷移到新系統的數據質量。
6.數據審計與錯誤追溯
在做完數據遷移之后,我們需要進行數據審計及錯誤追溯,以保證遷移過程沒有丟失數據。在第一批數據遷移完成后,QA同學經常抱怨說現在對數據審計及錯誤追溯非常不友好。原因是我們對第一批數據遷移的設計是,源數據轉換后直接生成目標文件。這導致進行審計以及出錯排查的時候,需要對文件直接進行分析。而分析文件有兩個非常不方便的點:
- 生產環境不允許下載文件,只得在生產環境上搭建用于審計及錯誤排查的腳本運行環境
- 即使是測試環境允許下載文件,有些實體數據量非常大,文件無法直接打開,還是得寫腳本進行分析
為了解決這個痛點,從第二批數據遷移開始,我們首先將轉換后的數據存入中間表,這樣也就對遷移過程做了一次解耦。事實證明解耦后我們對數據庫直接進行計數對及查詢,對數據審計及錯誤排查的效率有非常大的提升。
收獲
- 數據遷移過程涉及在三個系統之間傳輸數據。以及AWS到GCP的云平臺間的遷移,在這個復雜的過程遇到了大大小小數不盡的坑,也順勢學到了很多東西。
- 遷移并不是一錘子買賣,為了確保用戶的無縫過渡,在遷移過程中保證多個系統的可用性及系統間的數據一致性也非常重要。
- 實體間依賴對遷移計劃影響很大。在遷移過程中,需要建立了依賴映射并監控了每個實體的遷移歷史,以檢查每個記錄的實體關系/依賴關系。每當出現錯誤時,無效數據和依賴于無效數據的數據將被過濾掉,以確保整個遷移過程能夠繼續進行。
- 保持數據可追溯性是遷移過程的關鍵方面。通過確保所有數據都可以追溯到其來源,可以幫助最大限度地減少數據丟失或損壞的風險,并確保在整個遷移過程中數據的準確性和完整性
- 為確保成功的遷移,需要對復雜的業務邏輯進行徹底分析,并確定在過程中可能出現的任何潛在問題。這包括將所有必要的字段映射到新系統,并解決源數據中可能存在的任何數據質量問題。
- 為了確保遷移過程中數據的質量,重要的是制定一個測試策略,并由團隊中的每個人進行審查和維護。遷移的驗證過程需要充分考慮數據完整性和無數據丟失,并確保在遷移后滿足所有指定的功能和非功能方面的要求。