概述
目前占业,在利用hive建設數(shù)據(jù)倉庫的過程中,總會遇見分區(qū)分桶的赦肋,跟傳統(tǒng)的DBMS系統(tǒng)一樣块攒,通過表分區(qū)能夠在特定的區(qū)域檢索數(shù)據(jù),減少掃描成本佃乘,在一定程度上提高查詢效率囱井。比如我們要收集某個大型網(wǎng)站的日志數(shù)據(jù),一個網(wǎng)站每天的日志數(shù)據(jù)存在同一張表上趣避,由于每天會生成大量的日志庞呕,導致數(shù)據(jù)表的內(nèi)容巨大劫灶,在查詢時進行全表掃描耗費的資源非常多拓型。那其實這個情況下,我們可以按照日期對數(shù)據(jù)表進行分區(qū)策彤,不同日期的數(shù)據(jù)存放在不同的分區(qū)愁拭,在查詢時只要指定分區(qū)字段的值就可以直接從該分區(qū)查找讲逛。在物理上分區(qū)表會將數(shù)據(jù)按照分區(qū)鍵的列值存儲在表目錄的子目錄中,目錄名=“分區(qū)鍵=鍵值”岭埠。其中需要注意的是分區(qū)鍵的值不一定要基于表的某一列(字段)盏混,它可以指定任意值,只要查詢的時候指定相應的分區(qū)鍵來查詢即可惜论。
分桶與分區(qū)有所不同许赃,它指定分桶表的某一列,讓該列數(shù)據(jù)按照哈希取模的方式隨機来涨、均勻地分發(fā)到各個桶文件中图焰。因為分桶操作需要根據(jù)某一列具體數(shù)據(jù)來進行哈希取模操作,故指定的分桶列必須基于表中的某一列(字段)蹦掐。因為分桶改變了數(shù)據(jù)的存儲方式技羔,它會把哈希取模相同或者在某一區(qū)間的數(shù)據(jù)行放在同一個桶文件中。如此一來便可提高查詢效率卧抗,比如我們要對兩張在同一列上進行了分桶操作的表進行JOIN操作的時候藤滥,只需要對保存相同列值的桶進行JOIN操作即可。同時分桶也可以提高采樣率社裆。
分區(qū)
分區(qū)是為了對表進行合理的管理以及提高查詢效率拙绊,Hive可以將表組織成“分區(qū)”。一個分區(qū)實際上就是表下的一個目錄,一個表可以在多個維度上進行分區(qū)标沪,分區(qū)之間的關(guān)系就是目錄樹的關(guān)系榄攀。Hive(Inceptor)分區(qū)分為靜態(tài)分區(qū)跟動態(tài)分區(qū),詳細介紹如下:
靜態(tài)分區(qū)?????????????????????????????????????
靜態(tài)分區(qū)在插入或者導入的時候需要指定具體的分區(qū)
[if !supportLists]1金句、? [endif]靜態(tài)分區(qū)創(chuàng)建
需要在PARTITIONED BY后面跟上分區(qū)鍵檩赢,類型。例如:
CREATE?TABLE?p_table1(
id int
,name
string
)
PARTITIONED BY(date_day string)
stored as orc
;
這是一級分區(qū)违寞,當然也可以創(chuàng)建多級分區(qū)贞瞒。例如:
CREATE?TABLE? p_table1(
id int
,name string
)
PARTITIONED BY(date_day string, company
string,emp_no string)
stored as orc
;??????????????????????????????????????????????????????????????????????????
下面的實例都是以一級分區(qū)為例。?????????????????????
[if !supportLists]2趁曼、? [endif]靜態(tài)分區(qū)插入數(shù)據(jù)
insert overwrite table p_table1 partition(date_day='2019-07-14')
values(1,'lucy');
或者insert overwrite table p_table1 partition(date_day='2019-07-15')
select 2 as id,'lily' as name;
上面兩個例子都是覆蓋的形式军浆,也就是插入這個分區(qū)之前,會將該分區(qū)數(shù)據(jù)刪除挡闰,再插入新的數(shù)據(jù)乒融,也可以寫成追加的形式:
insert into p_table1
partition(date_day='2019-07-14') values(1,'lucy');
或者insert into e p_table1 partition(date_day='2019-07-15') select 2 as
id,'lily' as name;
[if !supportLists]3、? [endif]靜態(tài)分區(qū)查看分區(qū)
查看所有分區(qū)show partitions p_table1
結(jié)果如下:
date_day=2019-07-14
date_day=2019-07-15
查看某個分區(qū)show partitions p_table1 partition(date_day='2019-07-14');
結(jié)果如下:
date_day=2019-07-14
[if !supportLists]4尿这、? [endif]靜態(tài)分區(qū)刪除分區(qū)
刪除某個分區(qū)alter table p_table1 drop partition(date_day='2019-07-14');
或者刪除范圍內(nèi)的分區(qū)alter table p_table1 drop partition(date_day>='2019-07-14');
動態(tài)分區(qū)
1簇抵、動態(tài)分區(qū)創(chuàng)建
創(chuàng)建方式與靜態(tài)分區(qū)表完全一樣庆杜,一張表可同時被靜態(tài)和動態(tài)分區(qū)鍵分區(qū)射众,只是動態(tài)分區(qū)鍵需要放在靜態(tài)分區(qū)建的后面(因為HDFS上的動態(tài)分區(qū)目錄下不能包含靜態(tài)分區(qū)的子目錄)。
CREATE TABLE p_table2(
id int
,name string
)
PARTITIONED BY(date_day string,emp_no
string)
stored as orc
;
這是創(chuàng)建了二級分區(qū)表晃财。
2叨橱、動態(tài)分區(qū)插入數(shù)據(jù)
插入數(shù)據(jù)時需要開啟動態(tài)數(shù)據(jù)支持:
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nostrict;
插入數(shù)據(jù)(覆蓋)insert overwrite table p_table2 partition(date_day,emp_no)
select 2 as id,'lily' as name,'2019-07-14' as date_day, ‘a(chǎn)’ as emp_no;
分區(qū)并沒有寫死断盛,而是根據(jù)查詢到的值動態(tài)創(chuàng)建的兩級分區(qū)罗洗。
3、動態(tài)分區(qū)查看分區(qū)钢猛、刪除分區(qū)與靜態(tài)分區(qū)操作完全一致不再重述伙菜。
分桶
分桶字段是表內(nèi)字段,默認是對分桶的字段進行hash值命迈,然后姆啡疲總的桶數(shù),得到的值則是分區(qū)桶數(shù)壶愤,主要有以下兩點好處:
(1)獲得更高的查詢處理效率淑倾。桶為表加上了額外的結(jié)構(gòu),Hive 在處理有些查詢時能利用這個結(jié)構(gòu)征椒。具體而言娇哆,連接兩個在(包含連接列的)相同列上劃分了桶的表,可以使用 Map 端連接(Map-side join)高效的實現(xiàn)。比如JOIN操作碍讨。對于JOIN操作兩個表有一個相同的列治力,如果對這兩個表都進行了桶操作。那么將保存相同列值的桶進行JOIN操作就可以勃黍,可以大大較少JOIN的數(shù)據(jù)量琴许。
(2)使取樣(sampling)更高效。在處理大規(guī)模數(shù)據(jù)集時溉躲,在開發(fā)和修改查詢的階段榜田,如果能在數(shù)據(jù)集的一小部分數(shù)據(jù)上試運行查詢,會帶來很多方便锻梳。
創(chuàng)建分桶表
先看一下創(chuàng)建分桶表的創(chuàng)建箭券,分桶表的建表有三種方式:直接建表,CREATE TABLE LIKE 和 CREATE TABLE AS SELECT 疑枯,單值分區(qū)表不能用 CREATETABLE
AS SELECT 建表辩块。這里以直接建表為例:
create table b_table1(id int,name string)
clustered by (id) sorted by(id) into 4 buckets stored as textfile;
使用CLUSTERED BY 子句來指定劃分桶所用的列和要劃分的桶的個數(shù),當表分區(qū)時荆永,每個分區(qū)下都會有4個桶废亭。對于map端連接的情況,兩個表以相同方式劃分桶具钥。處理左邊表內(nèi)某個桶的 mapper知道右邊表內(nèi)相匹配的行在對應的桶內(nèi)豆村。因此,mapper只需要獲取那個桶 (這只是右邊表內(nèi)存儲數(shù)據(jù)的一小部分)即可進行連接骂删。這一優(yōu)化方法并不一定要求兩個表必須桶的個數(shù)相同掌动,兩個表的桶個數(shù)是倍數(shù)關(guān)系也可以。用HiveQL對兩個劃分了桶的表進行連接宁玫。
桶中的數(shù)據(jù)可以根據(jù)一個或多個列另外進行排序粗恢。由于這樣對每個桶的連接變成了高效的歸并排序(merge-sort), 因此可以進一步提升map端連接的效率。
向分桶表寫入數(shù)據(jù)
如何保證表中的數(shù)據(jù)都劃分成桶了呢欧瘪?把在Hive外生成的數(shù)據(jù)加載到劃分成桶的表中眷射,當然是可以的。其實讓Hive來劃分桶更容易佛掖。這一操作通常針對已有的表妖碉。
Hive并不檢查數(shù)據(jù)文件中的桶是否和表定義中的桶一致(無論是對于桶的數(shù)量或用于劃分桶的列)。如果兩者不匹配苦囱,在査詢時可能會碰到錯誤或未定義的結(jié)果嗅绸。因此,建議讓Hive來進行劃分桶的操作撕彤。
要向分桶表中填充成員鱼鸠,需要將 hive.enforce.bucketing 屬性設置為 true猛拴。這樣Hive 就知道用表定義中聲明的數(shù)量來創(chuàng)建桶。
下面有個未分桶的用戶表b_user_test蚀狰,數(shù)據(jù)如下:
1??????a
2??????b
3??????c
4??????d
5??????e
6??????f
7??????g
插入語句
INSERT OVERWRITE TABLE b_table1 SELECT *
FROM b_user_test;
查看文件結(jié)構(gòu)
dfs -ls/user/hive/warehouse/bucketed_users;
文件結(jié)構(gòu)如下所示:
?/user/hive/warehouse/b_table1/000000_0
?/user/hive/warehouse/b_table1/000001_0
?/user/hive/warehouse/b_table1/000002_0
?/user/hive/warehouse/b_table1/000003_0
查看文件000000_0
dfs -cat /user/hive/warehouse/bucketed_users/000000_0;
值為4 d說明文件000000_0存的是對分桶數(shù)求余等于0的那部分數(shù)據(jù)愉昆。
對桶中的數(shù)據(jù)進行采樣
對分桶進行查詢 tablesample(bucket x out of y on id):
x:表示查詢那個桶
y:表示建表指定的桶的總數(shù),如果不是建表時指定的桶的總數(shù),則會重新分桶麻蹋。
x不能大于y跛溉。
取第一個桶的數(shù)據(jù):
Sql:SELECT * FROM
b_table1 TABLESAMPLE(BUCKET 2 OUT OF 4 ON id);
結(jié)果:
5??????e
[if !supportLists]1???????????????????????[endif]a
當桶數(shù)不等于建表指定的桶的總數(shù)時
Sql:SELECT * FROM
b_table1 TABLESAMPLE(BUCKET 2 OUT OF 3 ON id);
結(jié)果:
4??????d
1??????a
7??????g
由結(jié)果可知,進行了重新分桶扮授,分成了三個桶芳室,取出第二個桶的數(shù)據(jù),也就是hash值對3求余等于1的那部分數(shù)據(jù)刹勃。
分桶比分區(qū)粒度更細堪侯,在每個分區(qū)了可以將數(shù)據(jù)進行分桶操作。