深入理解 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表壓縮後的數據)。通常做法是先分區後分桶。

 

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章