1. 程式人生 > >資料遷移經驗總結——億級別多表異構的資料遷移工作

資料遷移經驗總結——億級別多表異構的資料遷移工作

由於系統改版,最近三個月在做資料遷移工作,由於業務的特殊,基本將資料遷移所能踩的坑都踩了一遍,決定好好做個總結。

遷移型別——新老系統表結構變化較大的歷史資料

一、核心問題

1.新老表結構變化極大。新表是以deliver為核心,另外還涉及倉儲系統的一張表,訂單系統的4張表,並按照新的邏輯對映關係進行遷移。
2.增量資料遷移。在全量資料遷移時必然會有新的資料,這些資料應該實時進行遷移
3.億級別資料效能、效率的考慮。由於訂單業務非常重要,資料遷移帶來的qps對資料庫的壓力非常大,需要不斷測試迭代找到一個適合的遷移效率和效能。
4.老資料格式問題。12、13年的資料由於歷史悠久,會存在缺資料,資料不全的問題,這些問題都應該在程式中進行容錯。
5.核對資料比較困難。由於新表涉及多箇舊表且資料量龐大,資料核對比較困難,目前自己寫指令碼按周並根據一定的邏輯進行資料核對,同時輔以肉眼比對。

二、資料遷移流程

全量同步 java程式

增量同步 otter + 擴充套件的java程式

歷史資料 需同步到 後端backend 和 前端cobar庫 

1.歷史資料按年份下載成多個文字(1個文字2g左右)

2.多執行緒讀文字資料

3.處理型別主要有insert batch_insert update ,根據型別不同,執行緒呼叫不同邏輯方法處理資料並寫入新表

三、展開討論

將上面問題進行展開,並總結其中的技術問題

1.專案架構層次

   該遷移專案採用java開發,僅僅作為遷移,定位輕量級,後端db採用jdbcTemplate , spring管理javabean 分為三層, 一層manager主要對資料流根據型別分發給不同業務邏輯處理 一層業務邏輯——處理對映關係 一層dao。

2.db相關問題

   後臺db 是一主多從 主用來接受寫請求,從都是讀請求。 從庫效能遠遠低於主庫

  2.1 呼叫介面取資料的問題

   剛開始做時,由於想把專案做的很輕,獲取老資料採用調介面的形式,呼叫order介面、wms介面獲取就可以獲取相應資料。但介面是很脆弱的,經統計http介面每秒只能抗500-1000左右的請求,大資料量下介面很快就成為瓶頸。我測試過一次,1億資料需要2周時間才能導完,所以必須放棄呼叫介面,自己讀db進行查詢。

  2.2 冷熱庫問題

   歷史資料大部分在冷庫裡,在db查詢是需要先查熱庫再查冷庫,並按照相應的邏輯進行查庫。

  2.3 batch批量插入 主從延遲問題

      一開始資料處理一條插入一條,這樣1億資料要產生1億條insert語句。對於主庫來說寫入是沒有影響的,但是從庫有很大問題,其效能和主庫相差深遠,同時在同步binlog上會產生很大的延時,一億insert意味著一億次commited 一億次提交 binlog同步時就會進行1億次網路傳輸,從庫也會執行1億次insert。這時候延遲還會產生更大的問題,因為主庫還有其他業務資料寫入,從庫沒有實時同步,導致客服查不到訂單。

     後來改成多value的insert語句,每次處理一批資料,一個commit提交。這樣即提高了insert插入效能,又解決了延遲問題。

     jdbcTemplate改為updateBacth()方法,但發現sql語句還是一個數據一個insert,這是必須讓jdbc如何支援batch insert, 需要在jdbc連線加上引數 jdbc:mysql://.....&rewriteBatchedStatements=true ;

    2.4  sql的dead connection

    多執行緒讀資料時,發現有幾個執行緒一直阻塞了,jstack後,發現Mysql報sokcetAvaiable,這個錯誤一般發生在連線前端cobar時。這是因為jdbc連線沒有設定連線超時,要加上connectTime socketTIme,這個是自己應用連線資料庫的網路超時時間,如果不設定就會一直等待下去。

      設定引數:&failOverReadOnly=false&connectTimeout=60000&socketTimeout=60000 failOverReadOnly jdbc預設超時重試就變為只讀了,必須加上failOverReadOnly引數

   3. 程式級優化

    3.1 全量資料的多執行緒解決方案

    單程序遷移,1億資料大概需要1周時間,時間和效率都太低了,所以思考改為多執行緒。其實改多執行緒很簡單,歷史資料是文字形式,只需要多執行緒去逐一讀文字,併發處理就可以了,同時採用countdownlatch保證執行緒全部完成後在退出主程式。

    3.2 增量資料的多執行緒解決方案

    otter會順序的消費binlog,消費過程是很快的,但如果採用單程序模式,瓶頸會卡在我的寫入程式上,所以將程式該為多執行緒。otter每次讀取2000個binlog記錄,主程序一旦獲得滿2000個後,再啟動多執行緒去併發處理這些記錄。還需要注意問題,有可能同一時間段針對一條記錄有多個改動,需要以最後一次改動為準,所以我將執行緒個數最為桶的個數,按照order_id和桶的餘數放入到對應的桶裡,同時通的value是一個map (order_id , row物件),這樣可以保證每一批處理都是最新的記錄。

    3.3 otter的擴充套件

    以後我會再寫一篇博文分析otter,這裡先簡要介紹一下。otter是阿里的一個開源專案,主要解決異地機房實時資料備份的問題,它的原理就是通過把自己偽裝成slave去讀取master的binlog然後解析成eventData物件並寫入新的表裡。它預設情況下僅支援一對一的複製、表的欄位對映邏輯非常簡單的複製。但阿里的專案就是強大,它給使用者提供了一個介面,繼承之後就可以按照自己的邏輯去同步資料了。繼承類是AbstractEventProcessor,這個類會接受到獲取的eventData物件(就是binlog一條內容),這裡面有你訂閱的表的所有欄位值,sql型別等。繼承後,在該類的方法裡引入自己的同步程式,這樣otter訂閱的資料就會進入自己的程式了。

   4.資料補救策略

    資料遷移後依然會存在一些問題:

    1.增量過程中,由於網路等原因會造成binlog讀取的卡頓,canal會進行重試,但是貌似該時間點上的資料依然會丟失

    2.遷移過程中,有一些時間點的誤差導致資料丟失或異常。

    由於新舊錶結果相差很大,資料量也太大了,不能簡單用一條sql,這樣會拖庫。所以我寫程式一週一週核對資料,主要核對新表有舊錶沒有的,這些需要刪除;舊錶有新表沒有的,這些需要新增;新表舊錶status不一致的這些需要根據舊錶更新。

    小結:

        1.效能——在遷移前對mysql效能,資料量大小都沒有一定的認識,採用介面獲取資料後改用讀庫多執行緒,一前一後浪費了半個多月的時間,再做遷移一定要考慮好資料量,遷移時間,表結果,mysql效能。

        2.程式碼編寫——該程式耦合太嚴重,後期每增加一種操作型別,原始碼上都要加很多判斷,下次再寫必須要面向介面程式設計,使用繼承和工廠方法(參考秒殺專案);jdbcTample很輕量級,單sql多了後,程式碼量還是會上來,下次提前做好評估,用mybatis;校驗指令碼和執行指令碼過多,日誌列印也過多,下次還是採用log4j可輸出多文字,同時指令碼書寫上要更優雅和簡潔。

        3.核對資料——資料遷移是一件髒活累活,這次遷移有一大半時間在進行資料和對,不停寫sql,不停看校驗資料。以後再有類似的遷移必須要先規劃好,怎樣進行核對,例如選擇幾個關鍵欄位做hash。


參考文獻:

1.《關於資料遷移的方法、步驟和心得》http://m.blog.csdn.net/blog/baoqiangwang_11109/5492910

2.  Jdbc驅動的rewriteBatchedStatements引數 http://www.cnblogs.com/chenjianjx/archive/2012/08/14/2637914.html

3.  深入理解JDBC的超時設定 http://www.importnew.com/2466.html 

4. otter https://github.com/alibaba/otter