Hive 分桶表
簡介
本文主要介紹了Hive中分桶表的使用及作用
分桶和分割槽
分割槽提供了一個隔離資料和優化查詢的便利的方式.但是當分割槽的數量過多時,會產生過多的小分割槽,這樣會給namenode帶來較大的壓力.分桶試講資料集分解成更容易管理的若干部分的另一個技術.
使用
我們先準備我們將使用的分桶表的資料.
1,jack,2016/11/11
2,michael,2016/11/12
3,summer,2016/11/13
4,spring,2016/11/14
5,nero,2016/11/15
6,book,2016/12/21
7,node,2016/12/22
8,tony,2016/12/23
9,green,2016/12/24
10,andy,2016/12/25
11,kaith,2016/12/26
12,spring,2016/12/27
13,andy,2016/12/28
14,tony,2016/12/29
15,green,2016/12/30
16,andy,2016/12/31
17,kaith,2017/1/1
18,xiaoming,2017/1/2
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
如上所示,這是一張顧客資訊表,3個欄位分別代表顧客的id,name,birthday
建立桶表
桶表的建表語法和普通表類似,但是需要制定分桶的規則和桶的個數.
create table t_bucket(id int,name string,birthday string)
clustered by (id)
into 4 buckets
row format delimited fields terminated by ',';
- 1
- 2
- 3
- 4
我們設定桶表按照id進行分桶,桶內資料按照id進行排序.注意這裡的建表語句只是告訴hive,t_bucket這張表是應該按照這種方式去儲存,但是並不會在插入資料時幫我們去分桶儲存.我們來做個試驗:
我們將上面準備好的資料插入t_bucket表
load data local inpath '/home/spark/jar/testdata/Customer.txt' into table t_bucket;
- 1
然後我們到hdfs的目錄去檢視,發現並沒有安裝我們預先設計的方式去儲存資料,資料檔案個數為一個;
hive (test_neil)> dfs -ls /user/hive/warehouse/test_neil.db/t_bucket;
Found 1 items
-rwxr-xr-x 1 root staff 364 2017-02-05 12:44 /user/hive/warehouse/test_neil.db/t_bucket/Customer.txt
- 1
- 2
- 3
- 4
事實上hive採用的為讀時模式,他並不會去判斷插入表的資料是否符合元資料的資訊.因為我們使用load插入資料並不會產生reduce,資料量較小,只生成了一個數據檔案,因此這並不是一個分桶表.一般我們並不採用load的方式去載入資料到bucket表,我們採用insert的方式,使用select將資料變成我們分桶指定的模式.
正確的load方式
首先我們把資料匯入另外一張表中
create table t_temp(id int,name string,birthday string)
row format delimited fields terminated by ',';
load data local inpath 'home/spark/jar/testdata/Customer.txt' into table t_temp;
- 1
- 2
- 3
- 4
在我們匯入資料前,需要將hive.enforce.bucketing的值設定為true,
set hive.enforce.bucketing = true
- 1
這個引數將強制控制ruduce的個數去和我們指定的分桶個數相適配.
將t_bucket表truncate掉,再次匯入資料
truncate table t_bucket;
insert into table t_bucket
select id,name,birthday
from t_temp
cluster by id;
- 1
- 2
- 3
- 4
- 5
- 6
在job執行的log中,我們可以看到最終分桶的情況:
Loading data to table test_neil.t_bucket
Table test_neil.t_bucket stats: [numFiles=4, numRows=18, totalSize=346, rawDataSize=328]
- 1
- 2
我們再次去檢視t_bucket表的目錄發現,這張表的資料已經被分成了四份,這樣我們便成功的將檔案進行了分桶的操作;
hive (test_neil)> dfs -ls /user/hive/warehouse/test_neil.db/t_bucket;
Found 4 items
-rwxr-xr-x 1 root staff 78 2017-02-05 13:14 /user/hive/warehouse/test_neil.db/t_bucket/000000_0
-rwxr-xr-x 1 root staff 92 2017-02-05 13:14 /user/hive/warehouse/test_neil.db/t_bucket/000001_0
-rwxr-xr-x 1 root staff 98 2017-02-05 13:14 /user/hive/warehouse/test_neil.db/t_bucket/000002_0
-rwxr-xr-x 1 root staff 78 2017-02-05 13:14 /user/hive/warehouse/test_neil.db/t_bucket/000003_0
- 1
- 2
- 3
- 4
- 5
- 6
我們去檢視檔案的內容:
dfs -cat /user/hive/warehouse/test_neil.db/t_bucket/000000_0;
dfs -cat /user/hive/warehouse/test_neil.db/t_bucket/000001_0;
dfs -cat /user/hive/warehouse/test_neil.db/t_bucket/000002_0;
dfs -cat /user/hive/warehouse/test_neil.db/t_bucket/000003_0;
- 1
- 2
- 3
- 4
結果:
4/spring/2016/11/14
8/tony/2016/12/23
12/spring/2016/12/27
16/andy/2016/12/31
- 1
- 2
- 3
- 4
1/jack/2016/11/11
5/nero/2016/11/15
9/green/2016/12/24
13/andy/2016/12/28
17/kaith/2017/1/1
- 1
- 2
- 3
- 4
- 5
2/michael/2016/11/12
6/book/2016/12/21
10/andy/2016/12/25
14/tony/2016/12/29
18/xiaoming/2017/1/2
- 1
- 2
- 3
- 4
- 5
3/summer/2016/11/13
7/node/2016/12/22
11/kaith/2016/12/26
15/green/2016/12/30
- 1
- 2
- 3
- 4
我們可以看到,我們的客戶資料被分成了四份.那麼這四份是如何進行劃分的呢?其實我們已經制定了按照id進行劃分,因此hive使用hash雜湊的方式,將id個數對桶個數求餘數,我們id為18個,對桶個數(4個)求餘數,結果為4.這樣每個桶最少有4條資料,同時這樣的方式得到的桶內資料其實相當於是隨機的.
cluster by和distribute by
在上面的select語句中,我們使用了cluster by語句執行分桶的方式.我們發現其實桶內的資料是按照id欄位進行升序排列的.其實cluster by相當於distribute by+sort by.sort by預設按照升序進行排列.
sort by排序的為reducer內的資料,在這裡就是bucket內的資料.這樣資料是區域性有序的,而order by是全域性有序的.執行了order by,最後只能有個reduce,因為要做全域性的排序.
但是呢,distribute by+sort by的組合會更加的靈活,因為我們可以去按照id分桶,按照birthday去進行排序.我們可以做如下的試驗.
insert into table t_bucket
select id,name,birthday
from t_temp
distribute by id
sort by birthday desc;
- 1
- 2
- 3
- 4
- 5
我們再去執行select,發現數據是按照id進行分桶的,但是資料的排列順序其實是按照birthday進行降序排列的.
16 andy 2016/12/31
12 spring 2016/12/27
8 tony 2016/12/23
4 spring 2016/11/14
17 kaith 2017/1/1
13 andy 2016/12/28
9 green 2016/12/24
5 nero 2016/11/15
1 jack 2016/11/11
18 xiaoming 2017/1/2
14 tony 2016/12/29
10 andy 2016/12/25
6 book 2016/12/21
2 michael 2016/11/12
15 green 2016/12/30
11 kaith 2016/12/26
7 node 2016/12/22
3 summer 2016/11/13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
分桶的好處
1.分桶加快了join查詢的速度.
對於map端連線的情況,兩個表以相同方式劃分桶。處理左邊表內某個桶的 mapper知道右邊表內相匹配的行在對應的桶內。因此,mapper只需要獲取那個桶 (這只是右邊表記憶體儲資料的一小部分)即可進行連線。這一優化方法並不一定要求 兩個表必須桶的個數相同,兩個表的桶個數是倍數關係也可以.這樣便採用了Map-side join的方式,避免全表進行笛卡爾積的操作.
**關於桶內排序的意義:
桶中的資料可以根據一個或多個列另外進行排序。由於這樣對每個桶的連線變成了高效的歸併排序(merge-sort), 因此可以進一步提升map端連線的效率。**
**分桶個數:
如果兩個表的分桶個數之間沒有什麼倍數的關係,這樣分桶表在做join時並不會提升效率,因為資料隨機分發,桶和桶之間並沒有對應關係.**
2.使取樣(sampling)更加的高效
在處理大規模資料集時,在開發和修改查詢的階段,如果能在資料集的一小部分資料上試執行查詢,會帶來很多方便.
使用上面的t-bucket我們進行演示.
假如我們使用的為一個大規模的資料集,我們只想去抽取部分資料進行檢視.使用bucket表可以變得更加的高效
select * from t_bucket tablesample(bucket 1 out of 4);
select * from t_bucket tablesample(bucket 1 out of 4 on id);
- 1
- 2
這樣表示我們從bucket1開始取樣1個bucket的資料.
select * from t_bucket tablesample(bucket x out of y on xx);
- 1
x表示從哪個bucket進行抽樣,桶計數從1開始.y用來計算抽取資料的量,計算方式為分桶數/y.假設我們一共分了128個桶,y設定為32,則表示要抽取4個bucket,如果x為12,則抽取的資料來自於12/13/14/15.y的值可以不為桶個數的公約數,可以為任意值.