Hive 系列(五)—— Hive 分區表和分桶表

1、分區表

1.1 概念

Hive 中的表對應爲 HDFS 上的指定目錄,在查詢數據時候,默認會對全表進行掃描,這樣時間和性能的消耗都很是大。java

分區爲 HDFS 上表目錄的子目錄,數據按照分區存儲在子目錄中。若是查詢的 where 字句的中包含分區條件,則直接從該分區去查找,而不是掃描整個表目錄,合理的分區設計能夠極大提升查詢速度和性能。git

這裏說明一下分區表並 Hive 獨有的概念,實際上這個概念很是常見。好比在咱們經常使用的 Oracle 數據庫中,當表中的數據量不斷增大,查詢數據的速度就會降低,這時也能夠對錶進行分區。表進行分區後,邏輯上表仍然是一張完整的表,只是將表中的數據存放到多個表空間(物理文件上),這樣查詢數據時,就沒必要要每次都掃描整張表,從而提高查詢性能。github

1.2 使用場景

一般,在管理大規模數據集的時候都須要進行分區,好比將日誌文件按天進行分區,從而保證數據細粒度的劃分,使得查詢性能獲得提高。sql

1.3 建立分區表

在 Hive 中可使用 PARTITIONED BY 子句建立分區表。表能夠包含一個或多個分區列,程序會爲分區列中的每一個不一樣值組合建立單獨的數據目錄。下面的咱們建立一張僱員表做爲測試:shell

CREATE EXTERNAL TABLE emp_partition(
    empno INT,
    ename STRING,
    job STRING,
    mgr INT,
    hiredate TIMESTAMP,
    sal DECIMAL(7,2),
    comm DECIMAL(7,2)
    )
    PARTITIONED BY (deptno INT)   -- 按照部門編號進行分區
    ROW FORMAT DELIMITED FIELDS TERMINATED BY "\t"
    LOCATION '/hive/emp_partition';

1.4 加載數據到分區表

加載數據到分區表時候必需要指定數據所處的分區:數據庫

# 加載部門編號爲20的數據到表中
LOAD DATA LOCAL INPATH "/usr/file/emp20.txt" OVERWRITE INTO TABLE emp_partition PARTITION (deptno=20)
# 加載部門編號爲30的數據到表中
LOAD DATA LOCAL INPATH "/usr/file/emp30.txt" OVERWRITE INTO TABLE emp_partition PARTITION (deptno=30)

1.5 查看分區目錄

這時候咱們直接查看錶目錄,能夠看到表目錄下存在兩個子目錄,分別是 deptno=20deptno=30,這就是分區目錄,分區目錄下才是咱們加載的數據文件。apache

# hadoop fs -ls  hdfs://hadoop001:8020/hive/emp_partition/

這時候當你的查詢語句的 where 包含 deptno=20,則就去對應的分區目錄下進行查找,而不用掃描全表。數組

2、分桶表

1.1 簡介

分區提供了一個隔離數據和優化查詢的可行方案,可是並不是全部的數據集均可以造成合理的分區,分區的數量也不是越多越好,過多的分區條件可能會致使不少分區上沒有數據。同時 Hive 會限制動態分區能夠建立的最大分區數,用來避免過多分區文件對文件系統產生負擔。鑑於以上緣由,Hive 還提供了一種更加細粒度的數據拆分方案:分桶表 (bucket Table)。數據結構

分桶表會將指定列的值進行哈希散列,並對 bucket(桶數量)取餘,而後存儲到對應的 bucket(桶)中。oop

1.2 理解分桶表

單從概念上理解分桶表可能會比較晦澀,其實和分區同樣,分桶這個概念一樣不是 Hive 獨有的,對於 Java 開發人員而言,這多是一個天天都會用到的概念,由於 Hive 中的分桶概念和 Java 數據結構中的 HashMap 的分桶概念是一致的。

當調用 HashMap 的 put() 方法存儲數據時,程序會先對 key 值調用 hashCode() 方法計算出 hashcode,而後對數組長度取模計算出 index,最後將數據存儲在數組 index 位置的鏈表上,鏈表達到必定閾值後會轉換爲紅黑樹 (JDK1.8+)。下圖爲 HashMap 的數據結構圖:

圖片引用自:HashMap vs. Hashtable

1.3 建立分桶表

在 Hive 中,咱們能夠經過 CLUSTERED BY 指定分桶列,並經過 SORTED BY 指定桶中數據的排序參考列。下面爲分桶表建表語句示例:

CREATE EXTERNAL TABLE emp_bucket(
    empno INT,
    ename STRING,
    job STRING,
    mgr INT,
    hiredate TIMESTAMP,
    sal DECIMAL(7,2),
    comm DECIMAL(7,2),
    deptno INT)
    CLUSTERED BY(empno) SORTED BY(empno ASC) INTO 4 BUCKETS  --按照員工編號散列到四個 bucket 中
    ROW FORMAT DELIMITED FIELDS TERMINATED BY "\t"
    LOCATION '/hive/emp_bucket';

1.4 加載數據到分桶表

這裏直接使用 Load 語句向分桶表加載數據,數據時能夠加載成功的,可是數據並不會分桶。

這是因爲分桶的實質是對指定字段作了 hash 散列而後存放到對應文件中,這意味着向分桶表中插入數據是必然要經過 MapReduce,且 Reducer 的數量必須等於分桶的數量。因爲以上緣由,分桶表的數據一般只能使用 CTAS(CREATE TABLE AS SELECT) 方式插入,由於 CTAS 操做會觸發 MapReduce。加載數據步驟以下:

1. 設置強制分桶

set hive.enforce.bucketing = true; --Hive 2.x 不須要這一步

在 Hive 0.x and 1.x 版本,必須使用設置 hive.enforce.bucketing = true,表示強制分桶,容許程序根據表結構自動選擇正確數量的 Reducer 和 cluster by column 來進行分桶。

2. CTAS導入數據

INSERT INTO TABLE emp_bucket SELECT *  FROM emp;  --這裏的 emp 表就是一張普通的僱員表

能夠從執行日誌看到 CTAS 觸發 MapReduce 操做,且 Reducer 數量和建表時候指定 bucket 數量一致:

1.5 查看分桶文件

bucket(桶) 本質上就是表目錄下的具體文件:

3、分區表和分桶表結合使用

分區表和分桶表的本質都是將數據按照不一樣粒度進行拆分,從而使得在查詢時候沒必要掃描全表,只須要掃描對應的分區或分桶,從而提高查詢效率。二者能夠結合起來使用,從而保證表數據在不一樣粒度上都能獲得合理的拆分。下面是 Hive 官方給出的示例:

CREATE TABLE page_view_bucketed(
    viewTime INT, 
    userid BIGINT,
    page_url STRING, 
    referrer_url STRING,
    ip STRING )
 PARTITIONED BY(dt STRING)
 CLUSTERED BY(userid) SORTED BY(viewTime) INTO 32 BUCKETS
 ROW FORMAT DELIMITED
   FIELDS TERMINATED BY '\001'
   COLLECTION ITEMS TERMINATED BY '\002'
   MAP KEYS TERMINATED BY '\003'
 STORED AS SEQUENCEFILE;

此時導入數據時須要指定分區:

INSERT OVERWRITE page_view_bucketed
PARTITION (dt='2009-02-25')
SELECT * FROM page_view WHERE dt='2009-02-25';

參考資料

  1. LanguageManual DDL BucketedTables

更多大數據系列文章能夠參見 GitHub 開源項目大數據入門指南

相關文章
相關標籤/搜索