開源ETL工具kettle系列之常見問題

摘要:本文主要介紹使用kettle設計一些ETL任務時一些常見問題,這些問題大部分都不在官方FAQ上,你可以在kettle的論壇上找到一些問題的答案

 

1. Join
我得到A 數據流(不管是基於文件或數據庫),A包含field1 , field2 , field3 字段,然後我還有一個B數據流,B包含field4 , field5 , field6 , 我現在想把它們 ‘加’ 起來, 應該怎麼樣做.
這是新手最容易犯錯的一個地方,A數據流跟B數據流能夠Join,肯定是它們包含join key ,join key 可以是一個字段也可以是多個字段。如果兩個數據流沒有join key ,那麼它們就是在做笛卡爾積,一般很少會這樣。比如你現在需要列出一個員工的姓名和他所在部門的姓名,如果這是在同一個數據庫,大家都知道會在一個sql 裏面加上where 限定條件,但是如果員工表和部門表在兩個不同的數據流裏面,尤其是數據源的來源是多個數據庫的情況,我們一般是要使用Database Join 操作,然後用兩個database table input 來表示輸入流,一個輸入是部門表的姓名,另一個是員工表的姓名,然後我們認爲這兩個表就可以 ”Join” 了,我們需要的輸出的確是這兩個字段,但是這兩個字段的輸出並不代表只需要這兩個字段的輸入,它們之間肯定是需要一個約束關係存在的。另外,無論是在做 Join , Merge , Update , Delete 這些常規操作的時候,都是先需要做一個compare 操作的,這個compare 操作都是針對compare key 的,無論兩個表結構是不是一樣的,比如employee 表和department 表,它們比較的依據就是employee 的外鍵department_id , 沒有這個compare key 這兩個表是不可能連接的起來的.. 對於兩個表可能還有人知道是直接sql 來做連接,如果是多個輸入數據源,然後是三個表,有人就開始迷茫了,A表一個字段,B表一個字段,C表一個字段,然後就連Join操作都沒有,直接 database table output , 然後開始報錯,報完錯就到處找高手問,他們的數據庫原理老師已經在吐血了。如果是三個表連接,一個sql 不能搞定,就需要先兩個表兩個表的連接,通過兩次compare key 連接之後得到你的輸出,記住,你的輸出並不能代表你的輸入. 下面總結一下:
1. 單數據源輸入,直接用sql 做連接
2. 多數據源輸入,(可能是文本或是兩個以上源數據庫),用database join 操作.
3. 三個表以上的多字段輸出.

2. Kettle的數據庫連接模式
Kettle的數據庫連接是一個步驟裏面控制一個單數據庫連接,所以kettle的連接有數據庫連接池,你可以在指定的數據庫連接裏面指定一開始連接池裏面放多少個數據庫連接,在創建數據庫連接的時候就有Pooling 選項卡,裏面可以指定最大連接數和初始連接數,這可以一定程度上提高速度.

3. transaction
我想在步驟A執行一個操作(更新或者插入),然後在經過若干個步驟之後,如果我發現某一個條件成立,我就提交所有的操作,如果失敗,我就回滾,kettle提供這種事務性的操作嗎?
Kettle 裏面是沒有所謂事務的概念的,每個步驟都是自己管理自己的連接的,在這個步驟開始的時候打開數據庫連接,在結束的時候關閉數據庫連接,一個步驟是肯定不會跨session的(數據庫裏面的session), 另外,由於kettle是並行執行的,所以不可能把一個數據庫連接打開很長時間不放,這樣可能會造成鎖出現,雖然不一定是死鎖,但是對性能還是影響太大了。ETL中的事務對性能影響也很大,所以不應該設計一種依賴與事務方式的ETL執行順序,畢竟這不是OLTP,因爲你可能一次需要提交的數據量是幾百 GB都有可能,任何一種數據庫維持一個幾百GB的回滾段性能都是會不大幅下降的.

4. 我真的需要transaction 但又不想要一個很複雜的設計,能不能提供一個簡單一點的方式
Kettle 在3.0.2GA版中將推出一種新功能,在一個table output 步驟中有一個Miscellaneous 選項卡,其中有一個Use unique connections 的選項,如果你選中的話就可以得到一個transaction 的簡單版,
由於是使用的單數據庫連接,所以可以有錯誤的時候回滾事務,不過要提醒一點是這種方式是以犧牲非常大的性能爲前提條件的,對於太大的數據量是不適合的(個人仍然不建議使用這種方式)

5. temporary 表如何使用
我要在ETL過程中創建一箇中間表,當某個條件成立的時候,我要把中間表的數據進行轉換,當另一條件成立的時候我要對中間表進行另一個操作,我想使用數據庫的臨時表來操作,應該用什麼步驟。
首先從temp 表的生命週期來分,temp分爲事務臨時表和會話臨時表,前面已經解釋過了,kettle是沒有所謂事務的概念的,所以自然也沒有所謂的事務臨時表。Kettle的每個步驟管理自己的數據庫連接,連接一結束,kettle也就自然丟掉了這個連接的session 的handler , 沒有辦法可以在其他步驟拿回這個session 的handler , 所以也就不能使用所謂的會話臨時表,當你嘗試再開一個連接的時候,你可以連上這個臨時表,但是你想要的臨時表裏面的數據都已經是空的(數據不一定被清除了,但是你連不上了),所以不要設計一個需要使用臨時表的轉換
之所以會使用臨時表,其實跟需要 ”事務” 特性有一點類似,都是希望在ETL過程中提供一種緩衝。臨時表很多時候都不是某一個源表的全部數據的鏡像,很多時候臨時表都是很小一部分結果集,可能經過了某種計算過程,你需要臨時表無非是基於下面三個特性:
1. 表結構固定,用一個固定的表來接受一部分數據。
2. 每次連接的時候裏面沒有數據。你希望它接受數據,但是不保存,每次都好像執行了truncate table 操作一樣
3. 不同的時候連接臨時表用同一個名字,你不想使用多個連接的時候用類似與temp1 , temp2 , temp3 , temp4 這種名字,應爲它們表結構一樣。
既然臨時表不能用,應該如何設計ETL過程呢?(可以用某種詭異的操作搞出臨時表,不過不建議這樣做罷了)
如果你的ETL過程比較的單線程性,也就是你清楚的知道同一時間只有一個這樣的表需要,你可以創建一個普通的表,每次連接的時候都執行truncate 操作,不論是通過table output 的truncate table 選項,還是通過手工執行truncate table sql 語句(在execute sql script 步驟)都可以達到目的(基於上面的1,2 特性)
如果你的ETL操作比較的多線程性,同一時間可能需要多個表結構一樣並且裏面都是爲空的表(基於上面1,2,3特性),你可以創建一個 “字符串+序列”  的模式,每次需要的時候,就創建這樣的表,用完之後就刪除,因爲你自己不一定知道你需要多少個這種類型的表,所以刪除會比truncate 好一些。
下面舉個例子怎麼創建這種表:
你可以使用某種約定的表名比如department_temp 作爲department 的臨時表。或者
把argument 傳到表名,使用 department_${argument} 的語法, 
如果你需要多個這種表,使用一個sequence 操作+execute sql script 操作,execute sql script 就下面這種模式
          Create table_?  (…………..)
在表的名字上加參數,前面接受一個sequence 或類似的輸入操作.
需要注意的是這種參數表名包括database table input 或者execute sql script ,只要是參數作爲表名的情況前面的輸入不能是從數據庫來的,應爲沒有辦法執行這種preparedStatement  語句,從數據庫來的值後面的操作是 “值操作” ,而不是字符串替換,只有argument 或者sequence 操作當作參數纔是字符串替換. (這一點官方FAQ也有提到)

6. update table 和execute sql script 裏面執行update 的區別
執行update table 操作是比較慢的,它會一條一條基於compare key 對比數據,然後決定是不是要執行update sql , 如果你知道你要怎麼更新數據儘可能的使用execute sql script 操作,在裏面手寫update sql (注意源數據庫和目標數據庫在哪),這種多行執行方式(update sql)肯定比單行執行方式(update table 操作)快的多。
另一個區別是execute sql script 操作是可以接受參數的輸入的。它前面可以是一個跟它完全不關的表一個sql :
select field1, field2 field3 from tableA 
後面執行另一個表的更新操作:
update tableB set field4 = ? where field5=? And field6=?
        然後選中execute sql script 的execute for each row .注意參數是一一對應的.(field4 對應field1 的值,
field5 對應field2 的值, field6 對應field3 的值)

7. kettle的性能
kettle本身的性能絕對是能夠應對大型應用的,一般的基於平均行長150的一條記錄,假設源數據庫,目標數據庫以及kettle都分別在幾臺機器上(最常見的桌面工作模式,雙核,1G內存),速度大概都可以到5000 行每秒左右,如果把硬件提高一些,性能還可以提升 , 但是ETL 過程中難免遇到性能問題,下面一些通用的步驟也許能給你一些幫助.
儘量使用數據庫連接池
儘量提高批處理的commit size
儘量使用緩存,緩存儘量大一些(主要是文本文件和數據流)
Kettle 是Java 做的,儘量用大一點的內存參數啓動Kettle.
可以使用sql 來做的一些操作儘量用sql 
Group , merge , stream lookup ,split field 這些操作都是比較慢的,想辦法避免他們.,能用sql 就用sql
插入大量數據的時候儘量把索引刪掉
儘量避免使用update , delete 操作,尤其是update , 如果可以把update 變成先delete ,  後insert .
能使用truncate table 的時候,就不要使用delete all row 這種類似sql
合理的分區
如果刪除操作是基於某一個分區的,就不要使用delete row 這種方式(不管是delete sql 還是delete 步驟),直接把分區drop 掉,再重新創建
儘量縮小輸入的數據集的大小(增量更新也是爲了這個目的)
儘量使用數據庫原生的方式裝載文本文件(Oracle 的sqlloader , MySQL 的bulk loader 步驟)
儘量不要用kettle 的calculate 計算步驟,能用數據庫本身的sql 就用sql ,不能用sql 就儘量想辦法用procedure , 實在不行纔是calculate 步驟.
要知道你的性能瓶頸在哪,可能有時候你使用了不恰當的方式,導致整個操作都變慢,觀察kettle log 生成的方式來了解你的ETL操作最慢的地方。
遠程數據庫用文件+FTP 的方式來傳數據 ,文件要壓縮。(只要不是局域網都可以認爲是遠程連接)

8. 描述物理環境
源數據庫的操作系統,硬件環境,是單數據源還是多數據源,數據庫怎麼分佈的,做ETL的那臺機器放在哪,操作系統和硬件環境是什麼,目標數據倉庫的數據庫是什麼,操作系統,硬件環境,數據庫的字符集怎麼選,數據傳輸方式是什麼,開發環境,測試環境和實際的生產環境有什麼區別,是不是需要一箇中間數據庫(staging 數據庫) ,源數據庫的數據庫版本號是多少,測試數據庫的版本號是多少,真正的目標數據庫的版本號是多少……. 這些信息也許很零散,但是都需要一份專門的文檔來描述這些信息,無論是你遇到問題需要別人幫助的時候描述問題本身,還是發現測試環境跟目標數據庫的版本號不一致,這份專門的文檔都能提供一些基本的信息

9. procedure
爲什麼我不能觸發procedure?
這個問題在官方FAQ裏面也有提到,觸發procedure 和 http client 都需要一個類似與觸發器的條件,你可以使用generate row 步驟產生一個空的row ,然後把這條記錄連上procedure 步驟,這樣就會使這條沒有記錄的空行觸發這個procedure (如果你打算使用無條件的單次觸發) ,當然procedure 也可以象table input 裏面的步驟那樣傳參數並且多次執行.
另外一個建議是不要使用複雜的procedure 來完成本該ETL任務完成的任務,比如創建表,填充數據,創建物化視圖等等.

10. 字符集
Kettle使用Java 通常使用的UTF8 來傳輸字符集,所以無論你使用何種數據庫,任何數據庫種類的字符集,kettle 都是支持的,如果你遇到了字符集問題,也許下面這些提示可以幫助你:
1. 單數據庫到單數據庫是絕對不會出現亂碼問題的,不管原數據庫和目標數據庫是何種種類,何種字符集
2. 多種不同字符集的原數據庫到一個目標數據庫,你首先需要確定多種源數據庫的字符集的最大兼容字符集是什麼,如果你不清楚,最好的辦法就是使用UTF8來創建數據庫.
3.  不要以你工作的環境來判斷字符集:現在某一個測試人員手上有一個oracle 的基於xxx 字符集的已經存在的數據庫,並且非常不幸的是xxx 字符集不是utf8 類型的,於是他把另一個基於yyy字符集的oracle 數據庫要經過某一個ETL過程轉換到oracle , 後來他發現無論怎麼樣設置都會出現亂碼,這是因爲你的數據庫本身的字符集不支持,無論你怎麼設置都是沒用的. 測試的數據庫不代表最後產品運行的數據庫,尤其是有時候爲了省事把多個不同的項目的不相關的數據庫裝在同一臺機器上,測試的時候又沒有分析清楚這種環境,所以也再次強調描述物理環境的重要性.
4. 你所看到的不一定代表實際儲存的:mysql 處理字符集的時候是要在jdbc 連接的參數裏面加上字符集參數的,而oracle 則是需要服務器端和客戶端使用同一種字符集才能正確顯示,所以你要明確你所看到的字符集亂碼不一定代表真的就是字符集亂碼,這需要你檢查在轉換之前的字符集是否會出現亂碼和轉換之後是否出現亂碼,你的桌面環境可能需要變動一些參數來適應這種變動
5. 不要在一個轉換中使用多個字符集做爲數據源.

11. 預定義時間維
Kettle提供了一個小工具幫助我們預填充時間維,這個工具在kettle_home / samples / transformations / General – populate date dimension. 這個示例產生的數據不一定能滿足各種需要,不過你可以通過修改這個示例來滿足自己的需求.

12. SQL tab 和 Options tab
在你創建一個數據庫連接的時候除了可以指定你一次需要初始化的連接池參數之外(在 Pooling 選項卡下面),還包括一個Options 選項卡和一個 SQL 選項卡, Options 選項卡里面主要設置一些連接時的參數,比如autocommit 是on 還是off , defaultFetchSize , useCursorFetch (mysql 默認支持的),oracle 還支持比如defaultExecuteBatch , oracle.jdbc.StreamBufferSize, oracle.jdbc.FreeMemoryOnEnterImplicitCache ,你可以查閱對應數據庫所支持的連接參數,另外一個小提示:在創建數據庫連接的時候,選擇你的數據庫類型,然後選到Options 選項卡,下面有一個Show help text on options usage , 點擊這個按鈕會把你帶到對應各個數據庫的連接參數的官方的一個參數列表頁面,通過查詢這個列表頁面你就可以知道那種數據庫可以使用何種參數了.
對於SQL 選項卡就是在你一連接這個Connection 之後,Kettle 會立刻執行的sql 語句,個人比較推薦的一個sql 是執行把所有日期格式統一成同一格式的sql ,比如在oracle 裏面就是:
   alter session set nls_date_format = xxxxxxxxxxxxx
   alter session set nls_xxxxxxxxx = xxxxxxxxxxxx
這樣可以避免你在轉換的時候大量使用to_date() , to_char 函數而僅僅只是爲了統一日期格式,對於增量更新的時候尤其適用.

13. 數據複製
有的時候可能我們需要的是類似數據複製或者一個備份數據庫,這個時候你需要的是一種數據庫私有的解決方案,Kettle 也許並不是你的第一選擇,比如對於Oracle 來說,可能rman , oracle stream , oracle replication 等等, mysql 也有mysql rmaster / slave 模式的replication 等私有的解決方法,如果你確定你的需求不是數據集成這方面的,那麼也許kettle 並不是一個很好的首選方案,你應該諮詢一下專業的DBA人士也會會更好.

14. 如何控制版本變更
Kettle 的每一個transformation 和job 都有一個version 字段(在你保存的時候), 不過這個功能還不實用,如果你需要版本控制的話,還是建議你將transformation 和job 轉換成文本文件保存,然後用svn 或cvs 或任意你熟悉的版本控制系統將其保存,kettle 將在下一個版本加入版本控制的功能(做的更易用).

15. 支持的數據源
Kettle 支持相當廣的數據源,比如在數據庫裏面的一些不太常見的Access , MaxDB (SAP DB) , Hypersonic , SAP R/3 system , Borland Interbase , Oracle RDB , Teradata和3.0新加入的Sybase IQ .
另外還包括Excel , CSV , LDAP ,以及OLAP Server Mondrian , 目前支持Web Service 不過暫時還不支持SOAP.

16. 調試和測試
當ETL轉換出現不可預知的問題時,或是你不清楚某個步驟的功能是什麼的情況下,你可能需要創建一個模擬環境來調適程序,下面一些建議可能會有所幫助:
儘量使用generate row 步驟或者固定的一個文本文件來創建一個模擬的數據源
模擬的數據源一定要有代表性,數據集一定儘量小(爲了性能考慮)但是數據本身要足夠分散.
創建了模擬的數據集後你應該清楚的知道你所要轉換之後的數據時什麼樣的.

17. 錯誤處理
在ETL任務中由於數據問題出現轉換錯誤是一件非常正常的事情,你不應該設計一個依賴於臨時表或者擁有事務特點的ETL過程,面對數據源質量問題的巨大挑戰,錯誤處理是並不可少的,kettle同樣提供非常方便的錯誤處理方式,在你可能會出錯的步驟點擊右鍵選擇Define Error handing , 它會要求你指定一個處理error 的步驟,你可以使用文本文件或者數據庫的表來儲存這些錯誤信息,這些錯誤信息會包含一個id 和一個出錯的字段,當你得到這些錯誤信息之後就需要你自己分析出錯的原因了,比如違反主鍵約束可能是你生成主鍵的方式有錯誤或者本身的數據有重複,而違反外鍵約束則可能是你依賴的一些表裏面的數據還沒有轉換或者外鍵表本身過濾掉了這些數據. 當你調整了這些錯誤之後,確定所有依賴的數據都被正確的處理了.kettle user guide 裏面有更詳細的解釋,裏面還附帶了一個使用JavaScript 來處理錯誤的示例,這種方式可以作爲處理簡單數據質量的方式.

18. 文檔,文檔,文檔
Kettle 提供了豐富的文檔和使用手冊,小到一個數據庫連接怎麼連,大到一個功能怎麼實現,所有的參數列表,對話框的每一個輸入輸出代表什麼意思都有解釋,所以當你遇到問題你應該第一時間翻閱這些文檔,也許上面已經告訴你怎麼做了. 另外kettle 還有一個非常活躍的社區,你可以到上面提問,但是記住在你提問之前先搜索一下論壇看有沒有類似的問題已經問過了,如果沒有記得描述清楚你的問題

總結
本系列文章主要討論瞭如何使用kettle 來處理數據倉庫中的緩慢增長維,動態ETL如何設計,增量更新的一些設計技巧,在應用程序中如何集成kettle 以及在使用kettle 時的一些常見問題. 如果你正在尋找一個工具來幫助你解決數據庫的集成問題或是你打算建立一個商業智能項目的數據倉庫,那麼kettle是一個不錯的選擇,你不用支付任何費用就可以得到很多很多數據集成的特性,大量文檔和社區支持. 難道這些不就是你希望從一個商業工具上的到的嗎?還在等什麼 ,開始你的數據集成之旅吧

發佈了59 篇原創文章 · 獲贊 12 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章