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
node

相關文章
相關標籤/搜索