動(dòng)態(tài)分區(qū)插入
前面的示例中,用戶(hù)必須知道對(duì)哪個(gè)分區(qū)插入數(shù)據(jù),并且一條insert語(yǔ)句只能插入一個(gè)分區(qū)。如果想要加載到多個(gè)分區(qū),需要使用多條insert語(yǔ)句,如下:
FROM page_view_stg pvs
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country='US')
SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip WHERE pvs.country = 'US'
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country='CA')
SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip WHERE pvs.country = 'CA'
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country='UK')
SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip WHERE pvs.country = 'UK';
為了將數(shù)據(jù)加載到特定日期的所有國(guó)家分區(qū)中,需要為每個(gè)國(guó)家寫(xiě)insert語(yǔ)句。這樣很不方便,你得預(yù)先知道國(guó)家列表并且創(chuàng)建好分區(qū)。如果有一天國(guó)家列表改變了,你還得修改insert語(yǔ)句同時(shí)得創(chuàng)建對(duì)應(yīng)的分區(qū)。這樣做效率還低,每個(gè)insert語(yǔ)句都會(huì)變成MapReduce作業(yè)。
動(dòng)態(tài)分區(qū)插入(或者多分區(qū)插入)就是為解決上述問(wèn)題設(shè)計(jì)的,動(dòng)態(tài)分區(qū)插入會(huì)在掃描輸入表時(shí)動(dòng)態(tài)決定應(yīng)該創(chuàng)建和填充哪些分區(qū)。這是新加的特性,從0.6.0版本可用。使用動(dòng)態(tài)分區(qū)插入,會(huì)對(duì)輸入列的值進(jìn)行評(píng)估,然后決定每行數(shù)據(jù)應(yīng)該插入哪個(gè)分區(qū)。如果分區(qū)沒(méi)有創(chuàng)建,會(huì)自動(dòng)創(chuàng)建分區(qū)。使用這個(gè)特性只需要寫(xiě)一條insert語(yǔ)句。另外,因?yàn)橹挥幸粭linsert語(yǔ)句,也只有一個(gè)對(duì)應(yīng)的MapReduce作業(yè)。相比多條inser的情況,這個(gè)特性會(huì)顯著提升性能,減少Hadoop集群的工作負(fù)載。
下面是一個(gè)加載所有國(guó)家分區(qū)數(shù)據(jù)的示例,只用到一條insert語(yǔ)句:
FROM page_view_stg pvs
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country)
SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip, pvs.country
動(dòng)態(tài)分區(qū)插入和多條insert語(yǔ)句有一些語(yǔ)法差異:
- 在
PARTITION中添加了country,但是沒(méi)有相關(guān)值。在這種情況下,country就是動(dòng)態(tài)分區(qū)列。dt有值,它是靜態(tài)分區(qū)列。如果是動(dòng)態(tài)分區(qū)列,它的值將會(huì)來(lái)自輸入列。目前只允許動(dòng)態(tài)分區(qū)列是最后一列,因?yàn)榉謪^(qū)列的順序說(shuō)明了其層次順序(意味著dt是root分區(qū),country是child分區(qū)),不能寫(xiě)成(dt, country='US')。 - 在
select語(yǔ)句中添加了pvs.country列。這是動(dòng)態(tài)分區(qū)列對(duì)應(yīng)的輸入列。注意,不需要給靜態(tài)分區(qū)列添加輸入列,因?yàn)?code>PARTITION語(yǔ)句知道它的值。動(dòng)態(tài)分區(qū)值是按照順序查詢(xún)的,不是按照名稱(chēng),并且是select語(yǔ)句的最后一列。
自定義Map/Reduce腳本
用戶(hù)可以加入已定義的Map/Reduce,這個(gè)特性是Hive語(yǔ)言原生支持的。例如,想要運(yùn)行自動(dòng)的腳本map_script和reduce_script,用戶(hù)可以執(zhí)行以下命令,使用TRANSFORM語(yǔ)句嵌入腳本。
注意,在傳給腳本之前,列會(huì)被轉(zhuǎn)換為字符串并以TAB分隔,用戶(hù)腳本的標(biāo)準(zhǔn)輸出會(huì)被當(dāng)做以TAB分隔的字符串列。用戶(hù)腳本的標(biāo)準(zhǔn)錯(cuò)誤可以在Hadoop的任務(wù)詳情頁(yè)面看到。
FROM (
FROM pv_users
MAP pv_users.userid, pv_users.date
USING 'map_script'
AS dt, uid
CLUSTER BY dt) map_output
INSERT OVERWRITE TABLE pv_users_reduced
REDUCE map_output.dt, map_output.uid
USING 'reduce_script'
AS date, count;
示例map腳本(weekday_mapper.py)
import sys
import datetime
for line in sys.stdin:
line = line.strip()
userid, unixtime = line.split('\t')
weekday = datetime.datetime.fromtimestamp(float(unixtime)).isoweekday()
print ','.join([userid, str(weekday)])
當(dāng)然,MAP和REDUCE都是語(yǔ)法糖。內(nèi)部查詢(xún)可以寫(xiě)為:
SELECT TRANSFORM(pv_users.userid, pv_users.date) USING 'map_script' AS dt, uid CLUSTER BY dt FROM pv_users;
無(wú)模式map/reduce:如果"USING map_script"后沒(méi)有"AS"語(yǔ)句,Hive假定腳本的輸出包含2個(gè)部分:key,放在tab之前,value,除key之外其余都放在tab之后。這和指定了"AS key, value"是有區(qū)別的,因?yàn)槿绻卸鄠€(gè)tab的話(huà),value只會(huì)包含了第一個(gè)tab和第二個(gè)tab之間的部分。
用這種方式,用戶(hù)可以在不知道m(xù)ap輸出模式的情況下遷移老的map/reduce腳本。不過(guò)用戶(hù)還是需要知道reduce輸出模式,因?yàn)楸仨毰c我們要插入的表進(jìn)行匹配。通常分區(qū)列是排序列的前綴,但并不強(qiáng)制。
FROM (
FROM pv_users
MAP pv_users.userid, pv_users.date
USING 'map_script'
CLUSTER BY key) map_output
INSERT OVERWRITE TABLE pv_users_reduced
REDUCE map_output.dt, map_output.uid
USING 'reduce_script'
AS date, count;
DISTRIBUTE BY和SORT BY:不指定"cluster by",用戶(hù)可以指定"distribute by"和"sort by",分區(qū)列和排序列可以不同。
FROM pv_users
MAP pv_users.userid, pv_users.date
USING 'map_script'
AS c1, c2, c3
DISTRIBUTE BY c2
SORT BY c2, c1) map_output
INSERT OVERWRITE TABLE pv_users_reduced
REDUCE map_output.c1, map_output.c2, map_output.c3
USING 'reduce_script'
AS date, count;
Co-Groups
假設(shè)我們要用uid列組合actions_video和action_comments表的行,并且將數(shù)據(jù)發(fā)送到自定義的'reduce_script',可以使用如下方式:
FROM (
FROM (
FROM action_video av
SELECT av.uid AS uid, av.id AS id, av.date AS date
UNION ALL
FROM action_comment ac
SELECT ac.uid AS uid, ac.id AS id, ac.date AS date
) union_actions
SELECT union_actions.uid, union_actions.id, union_actions.date
CLUSTER BY union_actions.uid) map
INSERT OVERWRITE TABLE actions_reduced
SELECT TRANSFORM(map.uid, map.id, map.date) USING 'reduce_script' AS (uid, id, reduced_val);