1. 程式人生 > >Hive 分桶表

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的值可以不為桶個數的公約數,可以為任意值.