1. 程式人生 > >hive中簡單介紹分割槽表

hive中簡單介紹分割槽表


所介紹內容基本上是翻譯官方文件,比較膚淺,如有錯誤,請指正!
hive中建立分割槽表沒有什麼複雜的分割槽型別(範圍分割槽、列表分割槽、hash分割槽、混合分割槽等)。分割槽列也不是表中的一個實際的欄位,而是一個或者多個偽列。意思是說在表的資料檔案中實際上並不儲存分割槽列的資訊與資料。
下面的語句建立了一個簡單的分割槽表:

create table partition_test
(member_id string,
name string
)
partitioned by (
stat_date string,
province string)
row format delimited fields terminated by ',';

這個例子中建立了stat_date和province兩個欄位作為分割槽列。通常情況下需要先預先建立好分割槽,然後才能使用該分割槽,例如:

alter table partition_test add partition (stat_date='20110728',province='zhejiang');

這樣就建立好了一個分割槽。這時我們會看到hive在HDFS儲存中建立了一個相應的資料夾:

$ hadoop fs -ls /user/hive/warehouse/partition_test/stat_date=20110728
Found 1 items
drwxr-xr-x - admin supergroup 0 2011-07-29 09:53 /user/hive/warehouse/partition_test/stat_date=20110728/province=zhejiang

每一個分割槽都會有一個獨立的資料夾,下面是該分割槽所有的資料檔案。在這個例子中stat_date是主層次,province是副層次,所有stat_date='20110728',而province不同的分割槽都會在/user/hive/warehouse/partition_test/stat_date=20110728 下面,而stat_date不同的分割槽都會在/user/hive/warehouse/partition_test/ 下面,如:

$ hadoop fs -ls /user/hive/warehouse/partition_test/
Found 2 items
drwxr-xr-x - admin supergroup 0 2011-07-28 19:46 /user/hive/warehouse/partition_test/stat_date=20110526
drwxr-xr-x - admin supergroup 0 2011-07-29 09:53 /user/hive/warehouse/partition_test/stat_date=20110728

注意,因為分割槽列的值要轉化為資料夾的儲存路徑,所以如果分割槽列的值中包含特殊值,如 '%', ':', '/', '#',它將會被使用%加上2位元組的ASCII碼進行轉義,如:

hive> alter table partition_test add partition (stat_date='2011/07/28',province='zhejiang');
OK
Time taken: 4.644 seconds

$hadoop fs -ls /user/hive/warehouse/partition_test/
Found 3 items
drwxr-xr-x - admin supergroup 0 2011-07-29 10:06 /user/hive/warehouse/partition_test/stat_date=2011%2F07%2F28
drwxr-xr-x - admin supergroup 0 2011-07-28 19:46 /user/hive/warehouse/partition_test/stat_date=20110526
drwxr-xr-x - admin supergroup 0 2011-07-29 09:53 /user/hive/warehouse/partition_test/stat_date=20110728

我使用一個輔助的非分割槽表partition_test_input準備向partition_test中插入資料:

hive> desc partition_test_input;
OK
stat_date string
member_id string
name string
province string

hive> select * from partition_test_input;
OK
20110526 1 liujiannan liaoning
20110526 2 wangchaoqun hubei
20110728 3 xuhongxing sichuan
20110728 4 zhudaoyong henan
20110728 5 zhouchengyu heilongjiang

然後我向partition_test的分割槽中插入資料:

hive> insert overwrite table partition_test partition(stat_date='20110728',province='henan') select member_id,name from partition_test_input where stat_date='20110728' and province='henan';
Total MapReduce jobs = 2
...
1 Rows loaded to partition_test
OK

還可以同時向多個分割槽插入資料,0.7版本以後不存在的分割槽會自動建立,0.6之前的版本官方文件上說必須要預先建立好分割槽:

hive>
> from partition_test_input
> insert overwrite table partition_test partition (stat_date='20110526',province='liaoning')
> select member_id,name where stat_date='20110526' and province='liaoning'
> insert overwrite table partition_test partition (stat_date='20110728',province='sichuan')
> select member_id,name where stat_date='20110728' and province='sichuan'
> insert overwrite table partition_test partition (stat_date='20110728',province='heilongjiang')
> select member_id,name where stat_date='20110728' and province='heilongjiang';
Total MapReduce jobs = 4
...
3 Rows loaded to partition_test
OK

特別要注意,在其他資料庫中,一般向分割槽表中插入資料時系統會校驗資料是否符合該分割槽,如果不符合會報錯。而在hive中,向某個分割槽中插入什麼樣的資料完全是由人來控制的,因為分割槽鍵是偽列,不實際儲存在檔案中,如:


hive> insert overwrite table partition_test partition(stat_date='20110527',province='liaoning') select member_id,name from partition_test_input;
Total MapReduce jobs = 2
...
5 Rows loaded to partition_test
OK

hive> select * from partition_test where stat_date='20110527' and province='liaoning';
OK
1 liujiannan 20110527 liaoning
2 wangchaoqun 20110527 liaoning
3 xuhongxing 20110527 liaoning
4 zhudaoyong 20110527 liaoning
5 zhouchengyu 20110527 liaoning

可以看到在partition_test_input中的5條資料有著不同的stat_date和province,但是在插入到partition(stat_date='20110527',province='liaoning')這個分割槽後,5條資料的stat_date和province都變成相同的了,因為這兩列的資料是根據資料夾的名字讀取來的,而不是實際從資料檔案中讀取來的:

$ hadoop fs -cat /user/hive/warehouse/partition_test/stat_date=20110527/province=liaoning/000000_0
1,liujiannan
2,wangchaoqun
3,xuhongxing
4,zhudaoyong
5,zhouchengyu

下面介紹一下動態分割槽,因為按照上面的方法向分割槽表中插入資料,如果源資料量很大,那麼針對一個分割槽就要寫一個insert,非常麻煩。況且在之前的版本中,必須先手動建立好所有的分割槽後才能插入,這就更麻煩了,你必須先要知道源資料中都有什麼樣的資料才能建立分割槽。
使用動態分割槽可以很好的解決上述問題。動態分割槽可以根據查詢得到的資料自動匹配到相應的分割槽中去。 
使用動態分割槽要先設定hive.exec.dynamic.partition引數值為true,預設值為false,即不允許使用:

hive> set hive.exec.dynamic.partition;
hive.exec.dynamic.partition=false
hive> set hive.exec.dynamic.partition=true;
hive> set hive.exec.dynamic.partition;
hive.exec.dynamic.partition=true

動態分割槽的使用方法很簡單,假設我想向stat_date='20110728'這個分割槽下面插入資料,至於province插入到哪個子分割槽下面讓資料庫自己來判斷,那可以這樣寫:

hive> insert overwrite table partition_test partition(stat_date='20110728',province)
> select member_id,name,province from partition_test_input where stat_date='20110728';
Total MapReduce jobs = 2
...
3 Rows loaded to partition_test
OK

stat_date叫做靜態分割槽列,province叫做動態分割槽列。select子句中需要把動態分割槽列按照分割槽的順序寫出來,靜態分割槽列不用寫出來。這樣stat_date='20110728'的所有資料,會根據province的不同分別插入到/user/hive/warehouse/partition_test/stat_date=20110728/下面的不同的子資料夾下,如果源資料對應的province子分割槽不存在,則會自動建立,非常方便,而且避免了人工控制插入資料與分割槽的對映關係存在的潛在風險。

注意,動態分割槽不允許主分割槽採用動態列而副分割槽採用靜態列,這樣將導致所有的主分割槽都要建立副分割槽靜態列所定義的分割槽:

hive> insert overwrite table partition_test partition(stat_date,province='liaoning')
> select member_id,name,province from partition_test_input where province='liaoning';
FAILED: Error in semantic analysis: Line 1:48 Dynamic partition cannot be the parent of a static partition 'liaoning'

動態分割槽可以允許所有的分割槽列都是動態分割槽列,但是要首先設定一個引數hive.exec.dynamic.partition.mode :

hive> set hive.exec.dynamic.partition.mode;
hive.exec.dynamic.partition.mode=strict

它的預設值是strick,即不允許分割槽列全部是動態的,這是為了防止使用者有可能原意是隻在子分割槽內進行動態建分割槽,但是由於疏忽忘記為主分割槽列指定值了,這將導致一個dml語句在短時間內建立大量的新的分割槽(對應大量新的資料夾),對系統性能帶來影響。
所以我們要設定:
hive> set hive.exec.dynamic.partition.mode=nostrick;

再介紹3個引數:
hive.exec.max.dynamic.partitions.pernode (預設值100):每一個mapreduce job允許建立的分割槽的最大數量,如果超過了這個數量就會報錯
hive.exec.max.dynamic.partitions (預設值1000):一個dml語句允許建立的所有分割槽的最大數量
hive.exec.max.created.files (預設值100000):所有的mapreduce job允許建立的檔案的最大數量

當源表資料量很大時,單獨一個mapreduce job中生成的資料在分割槽列上可能很分散,舉個簡單的例子,比如下面的表要用3個map:
1
1
1
2
2
2
3
3
3

如果資料這樣分佈,那每個mapreduce只需要建立1個分割槽就可以了: 
         |1
map1 --> |1 
         |1 

         |2
map2 --> |2 
         |2 

         |3
map3 --> |3 
         |3
但是如果資料按下面這樣分佈,那第一個mapreduce就要建立3個分割槽: 

         |1
map1 --> |2 
         |3 

         |1
map2 --> |2 
         |3 

         |1
map3 --> |2 
         |3

下面給出了一個報錯的例子:
hive> set hive.exec.max.dynamic.partitions.pernode=4;
hive> insert overwrite table partition_test partition(stat_date,province)
> select member_id,name,stat_date,province from partition_test_input distribute by stat_date,province;
Total MapReduce jobs = 1
...
[Fatal Error] Operator FS_4 (id=4): Number of dynamic partitions exceeded hive.exec.max.dynamic.partitions.pernode.. Killing the job.
Ended Job = job_201107251641_0083 with errors
FAILED: Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.MapRedTask

為了讓分割槽列的值相同的資料儘量在同一個mapreduce中,這樣每一個mapreduce可以儘量少的產生新的資料夾,可以藉助distribute by的功能,將分割槽列值相同的資料放到一起:

hive> insert overwrite table partition_test partition(stat_date,province)
> select member_id,name,stat_date,province from partition_test_input distribute by stat_date,province;
Total MapReduce jobs = 1
...
18 Rows loaded to partition_test
OK

好了,關於hive的分割槽表先簡單介紹到這裡,後續版本如果有功能的更新我也會再更新。