1. 程式人生 > 其它 >資料插入動態分割槽

資料插入動態分割槽

(前人寫的不錯,很實用,負責任轉發)轉自:http://www.crazyant.net/1197.html

Hive的insert語句能夠從查詢語句中獲取資料,並同時將資料Load到目標表中。現在假定有一個已有資料的表staged_employees(僱員資訊全量表),所屬國家cnty和所屬州st是該表的兩個屬性,我們做個試驗將該表中的資料查詢出來插入到另一個表employees中。

1 2 3 4 INSERTOVERWRITETABLEemployees PARTITION(country='中國',state='北京') SELECT *FROMstaged_employeesse
WHEREse.cnty='中國'ANDse.st='北京';
由於使用了OVERWRITE關鍵字,目標表中原來相同partition中的所有資料被覆蓋,如果目標表中沒有partition,則整個表會被覆蓋。

如果把OVERWRITE關鍵字刪掉,或者替換成INTO,則hive會追加而不是替代原分割槽或原表中的資料,這個特性在Hive v0.8.0之後才支援。

當資料已經存在於hdfs上但不是我們想要的格式的時候,當進行的計算需要分好多步驟有必要儲存中間資料的時候,或者原資料沒有分割槽、有很多無效列需要過濾的時候,可以使用insert..select句型來完成這一轉換過程。

由於一個國家有很多個省份,如果想根據(國家country,地區partition)兩個維度對資料進行分割槽的話,這條SQL語句的執行個數應該等於地區的數目,比如中國有23個省就要對該SQL語句執行23次。因此hive對這個SQL語句進行了改造,只需要掃描一次原表就可以生成不同的輸出(多路輸出)。比如下面的SQL語句掃描了一次原始資料表,但是同時生成了3個省份的結果資料:

1 2 3 4 5 6 7 8 9 10 FROMstaged_employeesse INSERTOVERWRITETABLEemployees PARTITION(country='中國',state='河北省') SELECT *WHEREse.cnty='中國'ANDse.st='河北省' INSERTOVERWRITETABLEemployees PARTITION(country='中國',state='陝西省') SELECT *WHEREse.cnty='中國'ANDse.st='陝西省' INSERTOVERWRITETABLEemployees PARTITION(country='中國',state='河南省')
SELECT *WHEREse.cnty='US'ANDse.st='河南省';

通過縮排可以很清楚的看到,我們掃描了一次staged_employees表但是執行了3次不同的insert語句,這條大SQL語句是這麼執行的:先通過from staged_employees表獲取一條記錄,然後執行每一個select子句,如果select子句驗證通過則執行相應的insert語句。注意這裡的三條select子句是完全獨立執行的,並不是if .. then .. else的關係,這就意味著這3條select子句在某種情況下可能同時通過where檢測。

通過這種結構,原始表的資料能被拆分到目標表的不同partition中去。

如果原表的一條記錄滿足於其中一個給定的select .. where .. 子句,則該記錄將被寫到目標表的固定分割槽中。其實更進一步,每條Insert語句能將資料寫到不同的資料表中,不管這個表是否分割槽都一樣。

於是,就像一個過濾器一樣,原表的一些資料被寫到了很多輸出地址,而剩下的資料會被丟棄。

當然,你也可以混用Insert overwrite和insert into兩種不同的方法寫出資料。

向動態分割槽插入資料

但是問題還是沒有解決,中國有23個省,那麼我們就需要寫23個insert into .. select ..where子句,這非常不現實。於是hive的一種叫做動態分割槽的特性就出現了,它能夠根據select出來的引數自動推斷將資料插入到那個分割槽中去。本文上面的兩種SQL語句設定分割槽的方式都叫做靜態分割槽插入。

將上一個SQL語句進行改動,會得到以下簡潔的新SQL語句:

1 2 3 4 INSERTOVERWRITETABLEemployees PARTITION(country,state) SELECT...,se.cnty,se.st FROMstaged_employeesse;

hive先獲取select的最後兩個位置的se.cnty和se.st引數值,然後將這兩個值填寫到Insert語句partition中的兩個country和state變數中,即動態分割槽是通過位置來對應分割槽值的。原始表select出來的值和輸出partition的值的關係僅僅是通過位置來確定的,和名字並沒有關係,比如這裡se.cnty和county的名稱完全沒有關係。

上面的這條SQL語句是對兩個分割槽同時進行了動態設定,如果staged_employees表中有100個國家,每個國家有100個地區,那麼該SQL語句自動對每個國家和地區建立相應的partition並插入資料,如果用手寫的話不現實。

只要位置正確,你可以混用動態分割槽和靜態分割槽值設定,比如下面這個例子,你可以靜態指定一個country值,但是state值採用動態的方法設定:

1 2 3 4 5 INSERTOVERWRITETABLEemployees PARTITION(country='US',state) SELECT...,se.cnty,se.st FROMstaged_employeesse WHEREse.cnty='US';

注意:靜態分割槽值必須在動態分割槽值的前面!

使用hive動態分割槽的引數設定

動態分割槽功能預設是關閉的,而當它是開啟狀態時,預設會工作在“strict”模式下,這種模式下要求至少指定一個靜態分割槽的值。這樣做是為了防止設計了大量partition的糟糕情況,舉個例子你使用時間戳來進行分割槽,竟然每一秒鐘都產生一個分割槽!還有其他的一些屬性設定用來限制類似的情況出現,如下表所示:

名稱 預設值 描述
hive.exec.dynamic.partition false 設定為true用於開啟動態分割槽功能
hive.exec.dynamic.partition.mode strict 設定為nonstrict能夠讓所有的分割槽都動態被設定,否則的話至少需要指定一個分割槽值
hive.exec.max.dynamic.partitions.pernode 100 能被每個mapper或者reducer建立的最大動態分割槽的數目,如果一個mappre或者reducer試圖建立多餘這個值的動態分割槽數目,會引發錯誤
hive.exec.max.dynamic.partitions +1000 被一條帶有動態分割槽的SQL語句所能建立的動態分割槽總量,如果超出限制會報出錯誤
hive.exec.max.created.files 100000 全域性能被建立檔案數目的最大值,專門有一個hadoop計數器來跟蹤該值,如果超出會報錯

舉個例子,使用全動態分割槽的SQL語句序列如下所示,需要先設定一些必要的引數才可以:

1 2 3 4 5 6 7 8 9 sethive.exec.dynamic.partition=true; sethive.exec.dynamic.partition.mode=nonstrict; sethive.exec.max.dynamic.partitions.pernode=1000; ---文章來自瘋狂的螞蟻www.crazyant.net INSERTOVERWRITETABLEemployees PARTITION(country,state) SELECT...,se.cty,se.st FROMstaged_employeesse;

總結

使用from .. insert.. select ..where結構能夠從一個數據表中抽取資料,將結果插入到不同的表和分割槽中,而使用動態分割槽能夠讓hive根據select最末幾個位置的值自動設定目標分割槽的值,使用動態分割槽需要設定一些hive執行引數。

Winners see the gain; Losers see the pain.