最近在用kettle遷移數據,從對kettle一點不會到比較熟悉,對於期間的一些問題和坑作了記錄和總結,內容涵蓋了使用的經驗和技巧,踩到的坑、最佳實踐和優化先後結果對比。javascript

經常使用轉換組件java

計算造成新字段:只限算術運算,而且選擇固定
過濾記錄:元表某字段按照某個條件分流,知足條件的到一個表,不知足的到另外一個表,這兩個目標表都必須有。
Switch/Case:和過濾記錄相似,能夠多個條件判斷,而且有默認轉向條件,能夠完美替換過濾記錄組建
記錄分組:group by 組建未能正常按照預期理解運行
設置爲NULL:將某個特定值設置爲NULL
行扁平化:行扁平化,使用與某條件下某名稱對應的行數相同的狀況
行列轉換:行轉成列,使用Row Normalizer組件,事先必定要是根據分組字段排好序,關鍵字段就是name列字段,分組字段就是按照什麼分組,目標字段就是行轉列以後造成的字段列表。 8.字段選擇:選擇須要的目的列到目標表,而且量表的對應字段不同時能夠用來作字段映射
排序:分組前先排序能夠提升效率
條件分發:根據條件分發,至關與informatica的router組件
值映射:至關與oracle的decode函數,源和目標字段同名的話,只要寫源字段就能夠了
#經常使用輸入組件mysql

表輸入:源表輸入
文本文件輸入:文本文件輸入
xml文件輸入:使用Get Data From XML組件,能夠在其中使用xpath來選擇數據
JsonInput:貌似在中文環境下組件面板裏看不到,切換到英文模式就看到了
#經常使用輸出組件git

表輸出:表輸出
文本文件輸出:文本文件輸出
XML文件輸出:輸出的XML文件是按照記錄行存儲的,字段名爲元素名
Excel文件輸出:輸出的excel文件是按照記錄行存儲的,字段名爲元素名
刪除:符合比較條件的記錄將刪除
更新:注意兩個表都要有主鍵才能夠
插入/更新:速度太慢,不建議使用
檢查字段是否存在:若在則家一個標誌位,值能夠是Y/N
等值鏈接:有關聯關係字段能夠關聯,其它的不關聯。
笛卡爾鏈接:全部兩邊的記錄交叉鏈接
write to log:把數據輸出到控制檯日誌裏,通常調試時很經常使用
空操做:很經常使用,好比過濾數據,未過濾走正常流程,濾除的數據就轉向空操做。我喜歡在轉換裏用它作開始和結束之類須要分發或匯聚數據流的場景
#內置變量sql

Internal.Transformation.Name 當前轉換的名字
Internal.Job.Name 當前job名字
Internal.Job.Filename.Name job的文件名
#須要修改的配置數據庫

在java8裏-XX:MaxPermSize,-XX:PermSize已經去掉了,須要修改爲-XX:MetaspaceSize 和 -XX:MaxMetaspaceSize編程

生產環境和開發環境使用不一樣的數據庫鏈接數組

~/.kettle/kettle.properties裏設置key=value服務器

在kettle.properties中添加變量,而後在相似數據庫鏈接的地方能夠用${key}來使用,這樣能夠實現開發環境和生產環境配置的差別,就算往資源庫裏提交也能夠互不影響了oracle

kettle分頁問題

kettle循環分頁

首先弄一個轉換A,根據源表獲取記錄數,頁數,每頁記錄數,而後寫入系統變量,而後在job裏調用轉換A,再加一個轉換B來遷移數據(其中查詢sql要使用轉換A生成的系統變量),最後在job裏用一個javascript腳原本判斷查詢記錄數是不是0,若是是0就走執行成功,不然就繼續執行轉換B。

最關鍵的是判斷的js腳本,能夠參考

var prevRow=previous_result.getRows();//獲取上一個傳遞的結果,這種方案須要在轉換B中將記錄集複製爲結果,若是記錄集較多會形成內存溢出。就算在job裏執行也是如此
完整代碼:

if (prevRow==null && prevRow.size()==0){
false;
}else{
var startRow=parseInt(parent_job.getVariable("START_ROW", 0));
var pageSize=parseInt(parent_job.getVariable("PAGE_SIZE",1000));
startRow=startRow+pageSize;
parent_job.setVariable("START_ROW", startRow);
true;
}
kettle分頁循環的更高效的改進方案

在轉換裏,每執行一次有個SUCC_COUNT環境變量就+1,在job中用js腳本判斷成功數是否>=總記錄數,是就終止循環,否就起始行+每頁記錄數,下面是代碼

var startRow=parseInt(parent_job.getVariable("startrow"));
var totalItemCount=parseInt(parent_job.getVariable("totalitemcount"));
if (startRow >= totalItemCount){
false;
}else{
true;
}
對比前一種方案,改進方案一次遷移一萬條數據沒有壓力,並且cpu穩定在20%如下。

參數和變量

全局變量參數

在kettle.properties中配置,經過獲取環境變量組件來讀取,通常用來作數據庫鏈接配置等

位置參數(arguments參數)

最多支持10個,經過命令行參數的位置來區別,不是太好用

命名參數(named params)

經過 -param:name=value的方式設置參數,若是傳多個參數須要

-param:name1:value1 -param:name2:value2
配置方法

在轉換中雙擊空白處添加命名參數arg3,arg4,用的時候能夠 ${arg3},${arg4}來使用,注意:若是不直接執行轉換就不要配置轉換命名參數(轉換的命名參數和全局參數在調試時有時候會出現莫名其妙的衝突),建議使用全局參數來替代
在job中雙擊轉換,切換到命名參數頁,點擊獲取參數(arg3和arg4會出現到列表裏)注意:在使用全局參數的時候這步能夠省略
在job中雙擊空白處添加命名參數arg3,arg4,而後在調用kitchen.sh時經過 -param:arg3=abc -param:arg4=def來使用,注意:-param傳遞的命名參數必定要在job中事先定義才能夠。
命名參數能夠作變量使用,即${var}的方式來調用,若是是日期這樣必須包含'的場景,能夠用-param:date="2018-1-1 0:0:0"來表示,在sql裏用'${var}'來表示
kitchen經常使用命令

命令行執行job(repository模式)

./kitchen.sh -listrep

kitchen.sh -rep=<respository名字> -user=<respository登陸用戶名> -pass=<respository密碼> -level=<日誌級別> -job=<job名字> -logfile=<日誌文件路徑>

kitchen.sh -rep=olpbdb01 -user=admin -pass=admin -level=Basic -dir=/demo1 -job=demo1 //會執行repository上 /demo1/demo1.kjb
命令行執行job(文件模式)

kitchen.sh -file=/home/job/demo.kjb >> /home/job/log/demo.log
命令行執行轉換(respository模式)

pan.sh -rep=mysql -user=admin -pass=admin -dir=/fixbug -trans=f_loan_update -level=Basic -logfile=/data/kettle.log
命令行執行轉換(文件模式)

pan.sh -file /data/kettle/demo1/t_test_rep_mysql.ktr
三種增量同步的模式

時間戳增量同步:表中增長一個時間戳字段,每次更新值查詢update_time>上次更新時間的記錄。優勢速度快,實現簡單,缺點是對數據庫有侵入性,對於業務系統也須要更新時間戳,增長了複雜性。
觸發器增量同步:使用觸發器來監控數據變化,對數據庫有侵入性而且實現難度較大
全表增量同步:主要是用合併記錄來比對,優勢是數據庫侵入較小,實現簡單,缺點是性能較差。 我的觀點是全表比對要好一點,若是按照分頁的方式的化,二十幾萬條數據20分鐘能夠全同步完成。但全表增量同步只適合對實時性要求不高的場景。
幾個經常使用組件的用途

1.字段選擇:好比上一步驟有10個字段,下一步驟須要對其中某個字段作處理,就用字段選擇來選擇那個字段。還有,若是要合併記錄,也會在數據流中使用字段選擇選擇一下字段。還有就是字段選擇自帶刪除字段和修改字段類型和格式的功能

2.寫日誌:在處理數據時用寫日誌組建來記錄logger是個不錯的方法。

3.Switch/Case:和合並記錄配合使用能夠實現增量的數據插入/更新和刪除。用過濾記錄也能夠實現一樣功能

4.表輸出:實際就是向表裏insert數據,裏面有個[返回自動產生的關鍵字]功能很好用,至關與insert後馬上查詢的到剛剛自增的ID,省去了一部查詢操做。

5.更新,刪除:和名字一個意思

6.空操做:這個也頗有用。

7.記錄集鏈接:相似sql中的join操做,把兩個數據流的字段(類型相同,列數相同,位置相同且已經排序過)拼合到一塊兒。

8.分組:相似sql裏的group by,構成分組的字段是分組條件(若是沒有組可分但又要把每一行的數據都拼成一個串,能夠不設置分組條件),聚合部分的字段是相似在select部分須要用聚合函數處理的字段。在拼合in 條件時頗有用。

9.javascript腳本:這個很差用,能不用就不要用了。javascript組件支持將js變量轉成輸出字段。注意在轉換裏js腳本是每行執行一次。

10.獲取變量:若是有外部傳入的命名參數或者有環境變量,最好獲取變量是作爲流程的起點來使用。

11.設置變量:把某個字段轉成變量時能夠用。

12.表輸入

12.1.通常提供一個複雜的sql查詢,並且若是表輸入須要參數,那麼前一步驟必定是個獲取變量。

12.2.若是須要實現動態sql(即拼一個sql存入變量A,而後在表輸入裏執行${A}),必須用兩個轉換實現。

12.3.若是須要實現每行查詢一次(儘可能避免這樣作,太慢),能夠在表輸入中選中從步驟插入數據,並勾選執行每一行,在表輸入的前一個步驟使用選擇選擇表輸入的參數,在表輸入中用佔位符?來表示字段選擇中選擇的字段。

12.4.若是有可能,儘可能一次性的用表輸入完成全部的各種計算,轉換,排序,而儘可能避免使用kettle自帶組件,由於這樣速度快。

13.映射

13.1.能夠在轉換裏調用另外一個轉換,轉換中經過映射輸入規範來接收入參數(實際就是個表記錄集,在輸入規範裏定義的都是字段),用映射輸出規範來定義輸出數據集。這樣整個映射就能夠做爲一個步驟整合到一個轉換裏(有輸入和輸出)。映射能夠實現轉換流程邏輯的複用。

13.2..關於在同一個轉換的不一樣步驟中先修改變量而後再獲取變量(取得的是轉換剛開始執行時的值)不正確的問題,官方是這樣解釋的,在轉換開始時會有一些變量初始化,初始化以後一些轉換中的步驟並非順次執行的,因此沒法作到同一個轉換中在一個步驟。對於這種狀況須要拆成兩個抓換,先定義和初始化變量,而後再另外一個轉換中獲取變量,須要注意的是,若是是轉換中定義變量在子映射的獲取的話也是不行的。

14.執行結果裏面的Preview data很是好用,能夠跑起來查看每一個步驟的處理結果,若是發現一個步驟有數據,下一個步驟沒數據了,那麼多是有問題了。

15.對於執行時有錯誤的狀況,最好採用一張表來存儲執行除錯的數據,這對於無人職守遷移數據很重要。能夠作成一個子轉換來實現功能的複用。

16.對於javascript的調試,最好使用第三方的js開發工具來作,kettle自帶的js編輯器太垃圾了。

17.合併記錄時老是報NullPointerException,緣由是合併記錄的兩個來源可能有不存在的狀況,也多是兩個數據來源的排序不一致

18.轉換的配置裏的日誌能夠在線上部署的時候先禁用掉,有問題的時候能夠再打開(經過點擊鏈接線)

kettle的最佳實踐

啓動時

kettle不能加入到PATH裏去,加了執行 kitchen.sh -listrep找不到資源庫
在~/.kettle裏有重要的kettle.properties和repositories.xml文件,服務器部署的時候須要拷貝上去
spoon圖形界面通常用來調試,跑多條數據會很慢
我的認爲文件模式比repository模式好用點,repository模式老是莫名其妙的出問題,而且repository沒法保留變動歷史,但文件模式+git就能夠作到
Unable to get module class path. (java.lang.RuntimeException: Unable to open JAR file, probably deleted: error in opening zip file) 須要刪掉 <kettle_home>/system/karaf/caches/下的全部文件
啓動時閃退時須要刪掉~/.kettle/db.cache打頭的文件就能夠了。
防內存溢出和提升性能的處理辦法

數據量較大時必定要使用分頁機制,控制每一個批次導入5000~10000
須要在分頁循環中首先用一個獨立的轉換來計算出當前批次的用戶ID數組,頁碼數量,總記錄數以及維度表的數據,好比有日期維度表,那麼就須要算出當前批次要處理的日期時間數組,最後把這些數據存入到全局變量裏面去。這樣在後續步驟就能夠取出這些全局變量內容按照分頁批次進行遷移了。 2.分頁要經過一個表輸入根據傳入的每頁記錄數動態計算出總頁數,並把總頁數,總記錄數存入全局變量,而後每處理一行計數器加1,截止條件就是總記錄數<=處理過的記錄數,從而實現的分頁循環。
分頁變量務必要經過命名參數-param來傳遞,這樣在生產環境萬一碰到了數據過大形成內存泄漏,能夠經過參數快速調整
分頁須要動態在模型中計算出頁碼數和總記錄數,能夠用個sql來搞定
select count(1) totalitemcount,
round(CEIL(count(1)/${pagesize})) pagecount
from table_name
where create_time between unix_timestamp('${startdate}') and unix_timestamp('${enddate}')
以後的結果(totalitemcount,pagecount)用設置變量組件存入變量裏就ok了 5. 注意數據量較大時不要使用記錄複製到結果組件,否則必定會內存溢出 6. kettle的不少功能都有對應的純sql實現方法,好比加字段,好比排序和空值的處理,純sql的實現方式要比kettle的方式快不少,並且對內存的消耗也會小不少。 7. 能夠設置幾個變量來優化性能 KETTLE_MAX_LOG_SIZE_IN_LINES=5000 #內存裏最多記錄多少行日誌 KETTLE_MAX_LOG_TIMEOUT_IN_MINUTES=1440 #kettle日誌的保留時間,單位是分鐘 KETTLE_MAX_JOB_ENTRIES_LOGGED=1000 #內存中保留多少實體返回結果日誌 KETTLE_MAX_JOB_TRACKER_SIZE=1000 #內存裏最多保留多少job跟蹤記錄 KETTLE_MAX_LOGGING_REGISTRY_SIZE=1000 #內存裏記錄多少實體 來優化內使用狀況(在~/.kettle/kettle.properties裏設置)

遷移模型的設計原則

整個模型必須是job+多個轉換(除非是一次性工做能夠沒有job)
job能夠認爲是表級處理(即一次處理多行,全部組件都是對於多行的處理組件),轉換能夠認爲是行級處理(即一次處理一行,全部組件都是一次一行)
轉換分兩組,初始化變量用的轉換(至少有一個,也可能有多個,主看是否有新變量,由於新變量沒法在同一個轉換裏使用),和遷移數據用的轉換(看狀況,通常一個就夠了)
命名參數的選擇,即經過-param:varname=value的參數,通常須要有startdate,enddate,startrow,pagesize幾個就夠了
遷移的模式通常來講須要一個獨立的轉換根據日期區間計算出本次須要處理的業務ID數組(即先鎖定該批次要處理交易),而後第二個轉換根據事先鎖定的交易ID數組提取出日期時間數組,用戶ID數組,地區數組等。第三個轉換再使用前面兩步轉換裏提取的變量查詢數據進行遷移
在遷移i數據時,數據流分紅了新數據流(針對業務表)和舊數據流(針對事實/維度表),新數據流經過排序、分組、字段選擇和鏈接數據集join起來,而後經過合併記錄組件計算出每行記錄的flagfield(new/changed/deleted/identical的評判結論),而後經過Switch/Case或過濾記錄分別針對每種狀況進行處理(調用表輸出/更新/刪除)
若是轉換時涉及多個類別的數據要遷移到一張事實/維度表,不要拆成兩組job,能夠在一個job裏依次調用一組轉換,執行完一組再執行下一組,千萬不要並行,由於設置的全局變量名字都同樣,會出現衝突問題
變量的的使用

${Internal.Entry.Current.Directory}/test.ktr能夠表示當前目錄下的test.ktr,同時適配repository模式和local文件模式
關於變量的使和編程語言中的變量不太同樣,沒法使用在同一個轉換中定義和獲取當前轉換內修改過的變量,變通方法是拆成兩個轉換來使用,這問題卡了好幾天才找到緣由。
在job/轉換經過-param:varname=value的方式傳參時,若是發現變量沒法解析,那麼必定是job和轉換的命名參數裏沒有配置(雙擊空白處,有個命名參數頁籤....)
在job/轉換開始執行的時候經過日誌輸出一下用到的變量是個很好的習慣
做業和轉換都要有命名參數startrow,pagesize,startdate,enddate幾個,這樣能夠在調用的時候靈活控制分頁以及起止時間,靈活實現全量和增量遷移
對變量衝突的問題要當心,特別是同一個job並行處理多個轉換時更是如此,所以在job裏並行執行轉換時要格外當心。
寫變量時有對變量做用域的設置,推薦設置成Valid in the root job,不推薦Valid in the Java Virtual Matchine。
表輸入的處理

表輸入有個功能,能夠每行都執行一次查詢,這個功能不要用,太慢對內存佔用很高。
推薦使用記錄集鏈接的方法,好比A,B,C三個表要經過外鍵拼接在一塊兒插入到D表中,那麼能夠A,B,C三個表分別經過表輸入查詢出來,而後經過鏈接記錄集拼接到一塊兒作爲新數據(排序、字段對齊、類型要一致),而後查詢出D表作爲老數據(排序、字段對齊、類型要一致),而後經過合併記錄的方式對比新老數據,並根據flagfield的四個狀態值(new/update/delete/identical)來經過Switch/Case組件分別處理插入/更新/刪除和無變化四種狀況。處理完成後,記得startrow要加一,這樣作會顯著提高遷移性能。
表輸入最好選中忽略插入錯誤選項而且設置自動產生的關鍵字字段名稱,而且在下一步驟用Switch/Case判斷下這個自動產生的關鍵字字段的內容是不是null,不這樣作,當插入出錯時(不會在表輸出步驟報錯),錯誤會在表輸出的下一步驟報錯。
全量和增量遷移

全量遷移和增量遷移作到一塊兒能夠經過合併記錄+Switch/Case來判斷flagfied的值分別實現對應的插入/更新/刪除/無變化四種類型的數據處理。千萬不要將全量遷移和增量遷移分開,維護工做量太大了
映射功能

映射功能能夠提高整個模型的複用度,映射中的輸入就是外部查詢的業務數據(不一樣的業務數據sql不一樣,但對於初始化的維度數據必須一致),這樣能夠實現業務轉換和通用轉換的分離,極大的下降整個模型的複雜度和維護難度。
每一個維度表要使用一個獨立的轉換(內部實現新增/修改/刪除等功能),在事實表中調用維度錶轉換(每一個維度字段都要對應一個轉換)在業務模型查詢出本頁裏面須要處理的維度數據集,而後傳入映射(子轉換)並交由子轉換(輸入規範組件)作處理,每一個子轉換處理一個維度表,處理完的結果在子轉換中經過映射輸出規範組件輸出到父模型裏,而後父模型能夠繼續往下處理。
映射調試:首先輸入數據用文本輸出組件輸出成A.txt,而後在映射中同時添加映射輸入規範和文件輸入(使用A.txt)經過點擊鏈接禁用和啓用輸入規範和文件輸入實現調試映射和在父轉換中調試映射。
映射能夠提高模型的複用度,好比相似日期處理,地區處理,用戶處理這些通常都要抽取成可複用的模型,經過映射嵌入到別的模型裏去,這樣模型的層次比較清晰和簡潔,並且不顯得那麼亂
映射時的變量我推薦勾選默認的「從父轉換集成全部變量」,而不要每一個轉換裏都定義,當子轉換和父轉換中的變量同名時很容易出現稀奇古怪的問題
映射偶爾會出現不返回數據的狀況(重複執行可能又正常了,估計是kettle的bug),通過測試,在傳參的上一步加一個文件輸出會有改善(鏈接使用複製就能夠)
模型調試

完整模型是由一個job,多個轉換組成(轉換也分紅了業務轉換和共用轉換),執行遷移的時候經過job來執行(不要直接執行轉換),從邏輯上只要關注全局變量的內容和轉換的結果就能夠了。這樣對於調試來講效率較高。
整個完整的轉換中,全局變量是統一的,主要包含startdate,enddate,pagesize,全部轉換和job都使用這三個命名參數,在經過kitchen.sh執行時要經過-param傳入這三個變量
某個共用轉換須要另外一個轉換中的數據的時候,可使用文件保存輸出數據,而後在共用轉換中用文件輸入替代映射輸入規範的輸入參數
合併記錄時的幾個注意事項。

貌似kettle是用數據字段匹配的,關鍵字段必須能夠惟一肯定一條數據(相似聯合惟一索引的做用,但不要選事實表主鍵),若是關鍵字段是空,那麼合併的結果可能多條會合併成一條。數據字段是標識新舊數據須要比對哪些字段(也就是經過哪些字段來的到new/update/delete/identical的評判結論)。
合併記錄時數據字段的多少並不影響合併後的結果。
合併記錄最大的做用是對比兩個數據流的數據變化,自動識別出須要插入/更新/刪除和無變化的數據行,再配合Switch/Case組件分別實現insert、update和delete。
注意在合併記錄中不能有更新時間,不然會出現很奇怪的結果
合併記錄時須要注意一個是新舊數據源的排序必須相同,第二就是關鍵字和數據字段的選擇,這兩點作到告終果就是對的。
合併記錄後某些記錄的flagfield老是不正確(已經存在的數據flagfield是new,形成插入時惟一索引衝突),這說明前面步驟的排序方式不對
合併記錄若是出現相同的兩條數據flagfield一個是new另外一個是deleted,那說明關鍵字定義的幾個字段有差別
合併記錄後處理更新和刪除時條件部分選擇合併記錄的關鍵字保持一致
表輸出的處理

表輸出有個[返回一個自動產生的關鍵字]在insert後,能夠將主鍵值自動獲取到並填入到一個新字段,在後續的步驟能夠經過給字段賦值來回寫到主鍵字段,再經過字段選擇移除這個臨時生成的新字段,能夠減小一次查詢表的過程。
表輸出時有個選項是忽略插入錯誤,這個必定要打開,但要有區分,通常惟一索引異常是要忽略掉的,但其他不能忽略,這個問題能夠經過添加自定義錯誤來解決。
合併以前必定要用選擇字段對新舊數據流裏面的字段作一致化處理(元數據名字,類型,長度,精度,Binary to Normal)甚至字段的數量和順序都要嚴格匹配
合併以前字段必定要排序,而且排序規則徹底一致
合併記錄後,flagfield=identical的記錄主鍵會是0,這時候須要把老數據從新鏈接到合併以後的數據流就能夠了(注意排序和字段名字)
異常處理

表輸出異常,表輸出是支持異常的,組件上右鍵菜單裏選「定義錯誤處理」,能夠設置錯誤列名,字段名,描述等信息,在後續步驟中可使用過濾記錄來作甄別,好比主鍵衝突錯誤要Contains Duplicate entry就忽略掉異常,其他的錯誤就中止,這樣能夠提升容錯性。
排序

不少組件都要實現對數據排序,好比分組、合併記錄、鏈接數據集等,若是不排序會出現一些稀奇古怪的問題
有條件必定用sql排序,只有中間步驟無法使用sql排序的狀況下才使用kettle自帶的排序。
異常和報錯

you're mixing rows with different storage types. Field [VISIT_NO String(13)<binary-string>] does not have the same storage type as field [VISIT_NO String(13)]
有兩種辦法,一種是兩處數據流在查詢的時候,字段類型不一致,能夠在sql裏作下cast轉換解決。
另一種是經過字段選擇,在元數據頁添加一致的類型(長度,格式,最重要的是Binary to Normal改爲true)
java.lang.NullPointerException:
這種通常是有合併記錄步驟,須要兩個輸入步驟,但其中有的輸入步驟不存在,就會報這個錯
字段選擇提示字段不存在,但字段明明存在:
由於在選擇和修改頁把字段修改了名字,在元數據頁填修改後的新名字就不報錯了
優化先後效果對比

優化前

裏面有大量的每行查詢一次的功能,一次遷移最多2000條,多了就內存溢出
全量遷移一次要2天
全量和增量遷移是分開的,修改起來很羅嗦
沒有使用變量,limit限制條件都是寫死的,每次執行都要先調整模型的limit參數
沒有使用子轉換,模型間沒有複用,模型都是複製來複制去,很容易出錯
缺乏異常處理
優化後

增長命名參數,遷移的時候很是靈活改用分頁機制,每頁數據量能夠經過參數傳遞將全量模型和增量模型整合在一塊兒,經過時間參數來控制將用戶,時間,地區等複用度高的模型寫成了子轉換,經過映射的方式提升模型複用度,極大的簡化了模型。有異常處理,同時對插入數據作了容錯(索引衝突不報錯,認爲是成功)增長了完善的數據調試機制優化後每次遷移能夠5000~10000條(經過參數設置,若是中間出錯還能夠從上次斷開的地方繼續遷移),自動全量遷移,相同的數據量遷移一次只要1小時就搞定。