Hive 中的表對應爲 HDFS 上的指定目錄,在查詢數據時候,默認會對全表進行掃描,這樣時間和性能的消耗都很是大。java
分區爲 HDFS 上表目錄的子目錄,數據按照分區存儲在子目錄中。若是查詢的 where
字句的中包含分區條件,則直接從該分區去查找,而不是掃描整個表目錄,合理的分區設計能夠極大提升查詢速度和性能。git
這裏說明一下分區表並 Hive 獨有的概念,實際上這個概念很是常見。好比在咱們經常使用的 Oracle 數據庫中,當表中的數據量不斷增大,查詢數據的速度就會降低,這時也能夠對錶進行分區。表進行分區後,邏輯上表仍然是一張完整的表,只是將表中的數據存放到多個表空間(物理文件上),這樣查詢數據時,就沒必要要每次都掃描整張表,從而提高查詢性能。github
一般,在管理大規模數據集的時候都須要進行分區,好比將日誌文件按天進行分區,從而保證數據細粒度的劃分,使得查詢性能獲得提高。sql
在 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';
加載數據到分區表時候必需要指定數據所處的分區:數據庫
# 加載部門編號爲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)
這時候咱們直接查看錶目錄,能夠看到表目錄下存在兩個子目錄,分別是 deptno=20
和 deptno=30
,這就是分區目錄,分區目錄下才是咱們加載的數據文件。apache
# hadoop fs -ls hdfs://hadoop001:8020/hive/emp_partition/
這時候當你的查詢語句的 where
包含 deptno=20
,則就去對應的分區目錄下進行查找,而不用掃描全表。數組
分區提供了一個隔離數據和優化查詢的可行方案,可是並不是全部的數據集均可以造成合理的分區,分區的數量也不是越多越好,過多的分區條件可能會致使不少分區上沒有數據。同時 Hive 會限制動態分區能夠建立的最大分區數,用來避免過多分區文件對文件系統產生負擔。鑑於以上緣由,Hive 還提供了一種更加細粒度的數據拆分方案:分桶表 (bucket Table)。數據結構
分桶表會將指定列的值進行哈希散列,並對 bucket(桶數量)取餘,而後存儲到對應的 bucket(桶)中。oop
單從概念上理解分桶表可能會比較晦澀,其實和分區同樣,分桶這個概念一樣不是 Hive 獨有的,對於 Java 開發人員而言,這多是一個天天都會用到的概念,由於 Hive 中的分桶概念和 Java 數據結構中的 HashMap 的分桶概念是一致的。
當調用 HashMap 的 put() 方法存儲數據時,程序會先對 key 值調用 hashCode() 方法計算出 hashcode,而後對數組長度取模計算出 index,最後將數據存儲在數組 index 位置的鏈表上,鏈表達到必定閾值後會轉換爲紅黑樹 (JDK1.8+)。下圖爲 HashMap 的數據結構圖:
圖片引用自:HashMap vs. Hashtable
在 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';
這裏直接使用 Load
語句向分桶表加載數據,數據時能夠加載成功的,可是數據並不會分桶。
這是因爲分桶的實質是對指定字段作了 hash 散列而後存放到對應文件中,這意味着向分桶表中插入數據是必然要經過 MapReduce,且 Reducer 的數量必須等於分桶的數量。因爲以上緣由,分桶表的數據一般只能使用 CTAS(CREATE TABLE AS SELECT) 方式插入,由於 CTAS 操做會觸發 MapReduce。加載數據步驟以下:
set hive.enforce.bucketing = true; --Hive 2.x 不須要這一步
在 Hive 0.x and 1.x 版本,必須使用設置 hive.enforce.bucketing = true
,表示強制分桶,容許程序根據表結構自動選擇正確數量的 Reducer 和 cluster by column 來進行分桶。
INSERT INTO TABLE emp_bucket SELECT * FROM emp; --這裏的 emp 表就是一張普通的僱員表
能夠從執行日誌看到 CTAS 觸發 MapReduce 操做,且 Reducer 數量和建表時候指定 bucket 數量一致:
bucket(桶) 本質上就是表目錄下的具體文件:
分區表和分桶表的本質都是將數據按照不一樣粒度進行拆分,從而使得在查詢時候沒必要掃描全表,只須要掃描對應的分區或分桶,從而提高查詢效率。二者能夠結合起來使用,從而保證表數據在不一樣粒度上都能獲得合理的拆分。下面是 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';
更多大數據系列文章能夠參見 GitHub 開源項目: 大數據入門指南