1. 程式人生 > >Hive分桶(bucket)

Hive分桶(bucket)

一 什麼是桶的概念,和分割槽有啥區別?

對於每一個表或者分割槽,可以進一步細分成桶,桶是對資料進行更細粒度的劃分。預設時對某一列進行hash,使用hashcode對 桶的個數求模取餘,確定哪一條記錄進入哪一個桶。

Hive在查詢資料的時候,一般會掃描整個表的資料,會消耗很多不必要的時間。有些時候,我們只需要關心一部分資料,比如WHERE子句所接的查詢條件,那這時候這種全表掃描的方式是很影響效能的。從而引入了分割槽的概念。分割槽就是對某列有相同的資料或者某一個數據範圍的資料進行分類,這樣在查詢的時候,就可以只是針對分割槽查詢,從而不必全表掃描。

二 如何分桶? 如何匯入資料?

2.1匯入資料

CREATE TABLE IF NOT EXISTS t_movie(

idINT,

nameSTRING,

directorSTRING,

countrySTRING,

yearSTRING,

monthSTRING

)ROWFORMAT DELIMITED FIELDS TERMINATED BY ',';

COMMENT'Create Bucket Movie Table'

PARTITIONEDBY (area STRING)

CLUSTEREDBY (country) INTO 4 BUCKETS

ROWFORMAT DELIMITED FIELDS TERMINATED BY ','

STOREDAS ORC;

CLUSTEREDBY: 指定根據哪一列來劃分桶

INTOnum BUCKTES: 指定劃分幾個桶

2.2匯入資料

我們需要設定sethive.enforce.bucketing=true;

如果我們沒有設定hive.enfoce.bucketing這個引數,那麼我們需要設定和分桶個數相匹配的Reducer數目,set mapred.reduce.tasks=4,並且查詢的時候需要新增CLSUTERBY子句。

所以如果設定了我們查詢的時候就不必設定Reducer數目,和查詢的時候不必指定CLSUTRER BY子句。

INSERT INTO TABLE bucket_movie PARTITION(area='China') SELECT * FROM t_movie WHERE country = 'China';

INSERT INTO TABLE bucket_movie PARTITION(area='America') SELECT * FROM t_movie WHERE country = 'America';

INSERT INTO TABLE bucket_movie PARTITION(area='Japan') SELECT * FROM t_movie WHERE country = 'Japan';

三 分桶作用

3.1 獲得更高的查詢效率

我們試想一個場景:比如2張大表需要JOIN,JOIN的欄位比如是id,我們進行Reduce Side Join(ShuffleJoin)合適嗎?肯定不合適。

如果我們用Map Side Join呢?Map Side Join場景是小表Join大表比較適合,因為會把小表資料是通過DistributedCache 分發到各個Map

Side,然後載入到記憶體和每一個Map 任務處理的大表進行JOIN,這樣就不必要去做Reduce JOIN, 但是如果是大表就不太適合放到記憶體去了。

所以Bucket這時候在Map SideJoin就有勇武之地了。

原理:

2張表對於連線的欄位進行分桶,處理左邊表內某個桶的Mapper他知道右邊表內對應的行在對應的桶內,因此Mapper只需要獲取那個桶,然後取得資料進行JOIN

如圖示:

我們需要將使用者表和訂單表進行join。


如果我們沒有分桶,那麼這2張大表在JOIN的時候,效率是很低的。

那現在我們引入桶的概念,那麼使用者表按照id分桶,訂單表按照

cid分桶,那麼相同id都會歸入一個桶。那麼此時再進行JOIN的時候是按照桶來JOIN的,那麼大大減少了JOIN的數量。

但是這是需要一定條件的,否則JOIN出來的結果是不正確的:

使用者表的桶的個數必須是訂單表的倍數或者因子,也就是說訂單表100各個桶,那麼使用者表可以是200,300或者10,20,25,50

為什麼要這麼做呢?

我們知道分桶會對該列進行hash,然後根據桶的數量取餘計算每一個記錄落在哪一個桶。

比如A表4個桶,B表5個桶,那麼假設hash取餘之後的數A表肯定是0,1,2,3 那麼B表的話則是0,1,2,3,4

問題來呢?如果假設hashcode得到的值為8,那麼在A表,這條資料就落在0這個桶內,而在表則會落在3這個桶內.

如果要想Hive執行BucketMap Join,我們需要確保這個引數是否為true:

hive.optimize.bucketmapjoin= true

step1: 先將小表做map,資料放入hashtable,廣播到所有大表的Map端,大表map端接受了小表的hashtable,並不需要合併成一個大的hashtable,直接可以進行map操作,map操作會產生桶個數的Split,然後小表資料放入記憶體,然後大表對應的split拿出來判斷。但是這時候還是有可能記憶體不夠用,所以並沒有完全解決Map Side Join在小表完全裝在進記憶體的限制。

如果桶中的資料可以根據一個或多個列另外進行排序。由於這樣對每個桶的連線變成了高效的歸併排序(merge-sort), 因此可以進一步提升map端連線的效率。以下語法宣告一個表使其使用排序桶: 

CREATE TABLE table_name (col_name data_type,……)

CLUSTERED BY (col_name) SORTED BY(col_name ASC|DESC)

set hive.optimize.bucketmapjoin.sortedmerge = true;

sethive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHive

InputFormat;

3.2 方便我們抽樣

我們知道抽象:

SELECT * FROM table_name TABLESAMPLE(nPERCENT);

就是針對n%進行取樣

有了桶之後呢?

SELECT * FROM film TABLESAMPLE(BUCKET x OUTOF y)

x:表示從哪一個桶開始抽樣

y:抽樣因素,必須是桶數的因子或者倍數,假設桶數是100那麼y可以是200,10,20,25,5。假設桶數是100,y=25時抽取(100/25) = 4個bucket資料;當y=200的時候(100/200) = 0.5個bucket的資料