深入理解 Hive 分割槽分桶 (Inceptor)
為何分割槽分桶
我們知道傳統的DBMS系統一般都具有表分割槽的功能,通過表分割槽能夠在特定的區域檢索資料,減少掃描成本,在一定程度上提高查詢效率,當然我們還可以通過進一步在分割槽上建立索引進一步提升查詢效率。在此就不贅述了。
在Hive數倉中也有分割槽分桶的概念,在邏輯上分割槽表與未分割槽表沒有區別,在物理上分割槽表會將資料按照分割槽鍵的列值儲存在表目錄的子目錄中,目錄名=“分割槽鍵=鍵值”。其中需要注意的是分割槽鍵的值不一定要基於表的某一列(欄位),它可以指定任意值,只要查詢的時候指定相應的分割槽鍵來查詢即可。我們可以對分割槽進行新增、刪除、重新命名、清空等操作。因為分割槽在特定的區域(子目錄)下檢索資料,它作用同DNMS分割槽一樣,都是為了減少掃描成本。
分桶則是指定分桶表的某一列,讓該列資料按照雜湊取模的方式隨機、均勻地分發到各個桶檔案中。因為分桶操作需要根據某一列具體資料來進行雜湊取模操作,故指定的分桶列必須基於表中的某一列(欄位)。因為分桶改變了資料的儲存方式,它會把雜湊取模相同或者在某一區間的資料行放在同一個桶檔案中。如此一來便可提高查詢效率,如:我們要對兩張在同一列上進行了分桶操作的表進行JOIN操作的時候,只需要對儲存相同列值的桶進行JOIN操作即可。同時分桶也能讓取樣(Sampling)更高效。
分割槽
Hive(Inceptor)分割槽又分為單值分割槽、範圍分割槽。單值分割槽又分為靜態分割槽和動態分割槽。我們先看下分割槽長啥樣。如下,假如有一張表名為persionrank表,記錄每個人的評級,有id、name、score欄位。我們便可以建立分割槽rank(注意rank不是表中的列,我們可以把它當做虛擬列),並將相應資料匯入指定分割槽(將資料插入指定目錄)。
單值分割槽
單值分割槽根據插入時是否需要手動指定分割槽可以分為:單值靜態分割槽:匯入資料時需要手動指定分割槽。單值動態分割槽:匯入資料時,系統可以動態判斷目標分割槽。
單值分割槽表的建表方式有兩種:直接定義列和 CREATE TABLE LIKE。注意,單值分割槽表不能用 CREATE
TABLE AS SELECT 建表。而範圍分割槽表只能通過直接定義列來建表。
1、靜態分割槽建立
直接在 PARTITIONED BY 後面跟上分割槽鍵、型別即可。(分割槽鍵不能和任何列重名)
CREATE [EXTERNAL] TABLE <table_name> (<col_name> <data_type> [, <col_name> <data_type> ...]) -- 指定分割槽鍵和資料型別 PARTITIONED BY (<partition_key> <data_type>, ...) [CLUSTERED BY ...] [ROW FORMAT <row_format>] [STORED AS TEXTFILE|ORC|CSVFILE] [LOCATION '<file_path>'] [TBLPROPERTIES ('<property_name>'='<property_value>', ...)];
2、靜態分割槽寫入
-- 覆蓋寫入
INSERT OVERWRITE TABLE <table_name>
PARTITION (<partition_key>=<partition_value>[, <partition_key>=<partition_value>, ...])
SELECT <select_statement>;
-- 追加寫入
INSERT INTO TABLE <table_name>
PARTITION (<partition_key>=<partition_value>[, <partition_key>=<partition_value>, ...])
SELECT <select_statement>;
3、動態分割槽建立
建立方式與靜態分割槽表完全一樣,一張表可同時被靜態和動態分割槽鍵分割槽,只是動態分割槽鍵需要放在靜態分割槽建的後面(因為HDFS上的動態分割槽目錄下不能包含靜態分割槽的子目錄),如下 spk 即 static partition key, dpk 即 dynamic partition key。
CREATE TABLE <table_name>
PARTITIONED BY ([<spk> <data_type>, ... ,] <dpk> <data_type>, [<dpk>
<data_type>,...]);
-- ...略
4、動態分割槽寫入
靜態分割槽鍵要用 <spk>=<value> 指定分割槽值;動態分割槽只需要給出分出分割槽鍵名稱 <dpk>。
-- 開啟動態分割槽支援,並設定最大分割槽數
set hive.exec.dynamic.partition=true;
set hive.exec.max.dynamic.partitions=2000;
-- <dpk>為動態分割槽鍵, <spk>為靜態分割槽鍵
INSERT (OVERWRITE | INTO) TABLE <table_name>
PARTITION ([<spk>=<value>, ..., ] <dpk>, [..., <dpk>])
SELECT <select_statement>;
範圍分割槽
單值分割槽每個分割槽對應於分割槽鍵的一個取值,而每個範圍分割槽則對應分割槽鍵的一個區間,只要落在指定區間內的記錄都被儲存在對應的分割槽下。分割槽範圍需要手動指定,分割槽的範圍為前閉後開區間 [最小值, 最大值)。最後出現的分割槽可以使用 MAXVALUE 作為上限,MAXVALUE 代表該分割槽鍵的資料型別所允許的最大
值。
CREATE [EXTERNAL] TABLE <table_name>
(<col_name> <data_type>, <col_name> <data_type>, ...)
PARTITIONED BY RANGE (<partition_key> <data_type>, ...)
(PARTITION [<partition_name>] VALUES LESS THAN (<cutoff>),
[PARTITION [<partition_name>] VALUES LESS THAN (<cutoff>),
...
]
PARTITION [<partition_name>] VALUES LESS THAN (<cutoff>|MAXVALUE)
)
[ROW FORMAT <row_format>] [STORED AS TEXTFILE|ORC|CSVFILE]
[LOCATION '<file_path>']
[TBLPROPERTIES ('<property_name>'='<property_value>', ...)];
eg:多個範圍分割槽鍵的情況:
DROP TABLE IF EXISTS test_demo;
CREATE TABLE test_demo (value INT)
PARTITIONED BY RANGE (id1 INT, id2 INT, id3 INT)
(
-- id1在(--∞,5]之間,id2在(-∞,105]之間,id3在(-∞,205]之間
PARTITION p5_105_205 VALUES LESS THAN (5, 105, 205),
-- id1在(--∞,5]之間,id2在(-∞,105]之間,id3在(205,215]之間
PARTITION p5_105_215 VALUES LESS THAN (5, 105, 215),
PARTITION p5_115_max VALUES LESS THAN (5, 115, MAXVALUE),
PARTITION p10_115_205 VALUES LESS THAN (10, 115, 205),
PARTITION p10_115_215 VALUES LESS THAN (10, 115, 215),
PARTITION pall_max values less than (MAXVALUE, MAXVALUE, MAXVALUE)
);
分桶
說完分割槽,我們來繼續搞分桶。對Hive(Inceptor)表分桶可以將表中記錄按分桶鍵的雜湊值分散進多個檔案中,這些小檔案稱為桶。
建立分桶表
我們先看一下建立分桶表的建立,分桶表的建表有三種方式:直接建表,CREATE TABLE LIKE 和 CREATE TABLE AS SELECT ,單值分割槽表不能用 CREATETABLE AS SELECT 建表。這裡以直接建表為例:
CREATE [EXTERNAL] TABLE <table_name>
(<col_name> <data_type> [, <col_name> <data_type> ...])]
[PARTITIONED BY ...]
CLUSTERED BY (<col_name>)
[SORTED BY (<col_name> [ASC|DESC] [, <col_name> [ASC|DESC]...])]
INTO <num_buckets> BUCKETS
[ROW FORMAT <row_format>]
[STORED AS TEXTFILE|ORC|CSVFILE]
[LOCATION '<file_path>']
[TBLPROPERTIES ('<property_name>'='<property_value>', ...)];
分桶鍵只能有一個即<col_name>。表可以同時分割槽和分桶,當表分割槽時,每個分割槽下都會有<num_buckets> 個桶。我們也可以選擇使用 SORTED BY … 在桶內排序,排序鍵和分桶鍵無需相同。ASC 為升序選項,DESC 為降序選項,預設排序方式是升序。<num_buckets> 指定分桶個數,也就是表目錄下小檔案的個數。
向分桶表寫入資料
因為分桶表在建立的時候只會定義Scheme,且寫入資料的時候不會自動進行分桶、排序,需要人工先進行分桶、排序後再寫入資料。確保目標表中的資料和它定義的分佈一致。
目前有兩種方式往分桶表中插入資料:
方法一:開啟enforce bucketing開關。
SET hive.enforce.bucketing=true; ①
INSERT (INTO|OVERWRITE) TABLE <bucketed_table> SELECT <select_statement>
[SORT BY <sort_key> [ASC|DESC], [<sort_key> [ASC|DESC], ...]]; ②
方法二:將reducer個數設定為目標表的桶數,並在 SELECT 語句中用 DISTRIBUTE BY <bucket_key>對查詢結果按目標表的分桶鍵分進reducer中。
SET mapred.reduce.tasks = <num_buckets>;
INSERT (INTO|OVERWRITE) TABLE <bucketed_table>
SELECT <select_statement>
DISTRIBUTE BY <bucket_key>, [<bucket_key>, ...]
[SORT BY <sort_key> [ASC|DESC], [<sort_key> [ASC|DESC], ...]];
- 如果分桶表建立時定義了排序鍵,那麼資料不僅要分桶,還要排序
- 如果分桶鍵和排序鍵不同,且按降序排列,使用Distribute by … Sort by分桶排序
- 如果分桶鍵和排序鍵相同,且按升序排列(預設),使用 Cluster by 分桶排序,即如下:
SET mapred.reduce.tasks = <num_buckets>;
INSERT (INTO|OVERWRITE) TABLE <bucketed_table>
SELECT <select_statement>
CLUSTER BY <bucket_sort_key>, [<bucket_sort_key>, ...];
另外補充說明一下,在Hive(Inceptor)中,ORC事務表必須進行分桶(為了提高效率)。每個桶的檔案大小應在100~200MB之間(ORC表壓縮後的資料)。通常做法是先分割槽後分桶。