Hive技術文檔
——Author HuangFx 2013/01/29
Hive是什么?
Hive是蜂房的意思,為什么hadoop上的這層數據倉庫叫Hive?
因為生物學上蜂房是一個結構相當精良的建築,取名Hive足見則個數據倉庫在數據存儲上也是堪稱精良的。Hive是Facebook開發的構建於Hadoop集群之上的數據倉庫應用,它提供了類似於SQL語法的HQL語句作為數據訪問接口,這使得普通分析人員的應用Hadoop的學習曲線變緩。
第一:Hive是建立在 Hadoop 上的數據倉庫基礎構架。
第二:很低的學習代價便可以讓用戶在Hadoop中進行存儲、查詢和分析存儲的大規模數據。
簡單的理解:如果用戶只是需要完成大規模數據的分析這件事情,那么,你只要有一套hadoop環境+一個hive數據庫,只要你懂SQL,你不必懂MapReduce程序如何編程、hadoop如何工作,你的SQL需求將自動被編譯到整個集群中去進行分布式計算,以提高分析效率。
Hive是大數據的必然!
Hive是Facebook開發的構建於Hadoop集群之上的數據倉庫應用,它提供了類似於SQL語法的HQL語句作為數據訪問接口,這使得普通分析人員的應用Hadoop的學習曲線變緩。至於Facebook為什么使用Hadoop和Hive組建其數據倉庫,其內部人員分享了他們的一些經歷,大致的過程是如下的:
1,Facebook的數據倉庫一開始是構建於MySQL之上的,但是隨着數據量的增加某些查詢需要幾個小時甚至幾天的時間才能完成。
2,當數據量接近1T的時候,mysqld后台進程宕掉,這時他們決定將他們數據倉庫轉移到Oracle。當然這次轉移的過程也是付出了很大的代價的,比如支持的SQL方言不同,修改以前的運行腳本等等。
3,Oracle應付幾T的數據還是沒有問題的,但是在開始收集用戶點擊流的數據(每天大約400G)之后,Oracle也開始撐不住了,由此又要考慮新的數據倉庫方案。
4,內部開發人員花了幾周的時間建立了一個並行日志處理系統Cheetah,這樣的話勉強可以在24小時之內處理完一天的點擊流數據。
5,Cheetah也存在許多缺點。后來發現了Hadoop項目,並開始試着將日志數據同時載入Cheetah和Hadoop做對比,Hadoop在處理大規模數據時更具優勢,后來將所有的工作流都從Cheetah轉移到了Hadoop,並基於Hadoop做了很多有價值的分析。
6,后來為了使組織中的多數人能夠使用Hadoop,開發了Hive,Hive提供了類似於SQL的查詢接口,非常方便。與此同時還開發了一些其它工具。
7,現在集群存儲2.5PB的數據,並且以每天15TB的數據在增長,每天提交3000個以上的作業,大約處理55TB的數據...
現在很多大的互聯網公司出於成本考慮都在研究、使用Hadoop;數據的價值正得到越來越多的人的重視,而這種重視,又體現出Hadoop存在的巨大價值。
淘寶
使用hive的原因
1 超大數據集設計的計算擴展能力
2 支持HiveSql查詢——簡單,學習代價低
3 統一的元數據管理
淘寶對hive的貢獻
1 UDFs
2 建立、刪除臨時函數
3 GBK支持
4 完全JDBC
5 Multi Distinct Aggregation支持
6 認證和權限
7 bug fix
淘寶未來對hive的計划
1 Hive IDE
2 Multi Distinct Aggregation優化
3 Multi Group By優化
4 極限存儲的索引和文件
5 表統計信息的支持
6 采用TFile做列存儲
騰訊
TDM團隊在Hive上的工作
持續優化Hive的查詢優化器,使SQL更加高效的運行;
不斷引入一些database特性、包括hash join、列存儲、多存儲引擎、SQL/MED等,將成熟的數據庫/並行數據庫理論應用於Hive之上,使它的性能不斷與並行數據倉庫接近;
加強MapReduce層的優化,使MR執行框架更合理高效的執行Hive翻譯的MR代碼。
TDM團隊主要的工作是優化hive的查詢效率。
我司
金融數據信息的與日俱增,使得某些數據表變得極為龐大,這種大的規模程度用傳統的關系型數據庫查詢語句處理大表的關聯查詢已經遇到了瓶頸。金融數據大表的出現對hive在數據存儲中的應用與發展提供了先決條件。
數據量(行)
表1 2247667 224w
表2 85201483 8520w
表3 28376850 2837w
表4 28735268 2873w
Hive的階段進展
1 在hadoop的平台架構下,完成hive數據倉庫與mysql、sqlserver以及其他關系型數據的數據遷移。
2 學習和研究HiveSql查詢語句的優化。其實對於用戶來說,或者說對於Hive程序員來說,優化HiveSql語句是整個工作的主要任務。
3 學習和研究Hive執行過程的優化,這是比較高級別的優化,需要深入了解Hive和hadoop的原理和過程。
Hive的基礎架構
Hive的數據
元數據
(表相關信息)存在關系數據庫中。
為什么要存在RDBMS中,獲取元數據信息需要很低的延時,這在hdfs上是無法滿足。
分析HiveSQL之后生成的MapReduce任務在運行的時候如果需要訪問元數據信息時,它並不會直接去訪問MetaStore。那么,他們是如何獲得需要的元數據信息的呢?原來,當將生成的物理計划序列化到plan.xml的時候,已經將相應的元數據信息保存到了plan.xml中。而plan.xml文件之后會被放入Hadoop的分布式緩存中,所以MapReduce任務就可以從分布式緩存中獲得需要的元數據信息。
數據
存在hdfs中。
Hive 沒有專門的數據存儲格式,也沒有為數據建立索引,用戶可以非常自由的組織 Hive 中的表,只需要在創建表的時候告訴 Hive 數據中的列分隔符和行分隔符,Hive 就可以解析數據。
第一,不加外部鏈接創建的表,數據存儲在hive-site.xml配置的${hive.metastore.warehouse.dir} 指定的數據倉庫的目錄下;數據需要load。
例子:
<property>
<name>hive.metastore.warehouse.dir</name>
<value>/user/hive/warehouse</value>
<description>location of default database for the warehouse</description>
</property>
第二,加外部鏈接穿件的表,數據存在創建表時指定的hdfs路徑下。
數據的存儲關系
Partition 對應於數據庫中的 Partition 列的密集索引,但是 Hive 中 Partition 的組織方式和數據庫中的很不相同。在 Hive 中,表中的一個 Partition 對應於表下的一個目錄,所有的 Partition 的數據都存儲在對應的目錄中。例如:pvs 表中包含 ds 和 city 兩個 Partition,則對應於 ds = 20090801, ctry = US 的 HDFS 子目錄為:/wh/pvs/ds=20090801/ctry=US;對應於 ds = 20090801, ctry = CA 的 HDFS 子目錄為;/wh/pvs/ds=20090801/ctry=CA
Buckets 對指定列計算 hash,根據 hash 值切分數據,目的是為了並行,每一個 Bucket 對應一個文件。將 user 列分散至 32 個 bucket,首先對 user 列的值計算 hash,對應 hash 值為 0 的 HDFS 目錄為:/wh/pvs/ds=20090801/ctry=US/part-00000;hash 值為 20 的 HDFS 目錄為:/wh/pvs/ds=20090801/ctry=US/part-00020
這些信息屬於元數據信息,可以去mysql中查看。
Hive的執行原理
HiveSQL的數據類型
Primitive types:
· TINYINT
· SMALLINT
· INT
· BIGINT
· BOOLEAN
· FLOAT
· DOUBLE
· STRING
· BINARY(Note: Only available starting with Hive 0.8.0)
· TIMESTAMP(Note: Only available starting with Hive 0.8.0)
Complex types:
· arrays: ARRAY<data_type>
· maps: MAP<primitive_type, data_type>
· structs: STRUCT<col_name : data_type [COMMENT col_comment], ...>
· union: UNIONTYPE<data_type, data_type, ...>
符合類型的應用:
CREATE TABLE union_test(
foo UNIONTYPE<int, double, array<string>, struct<a:int,b:string>>);
SELECT foo FROM union_test;
{0:1}
{1:2.0}
{2:["three","four"]}
{3:{"a":5,"b":"five"}}
{2:["six","seven"]}
{3:{"a":8,"b":"eight"}}
{0:9}
{1:10.0}
HiveSQL和SQL
兩者大同小異,在語法上基本一致,在數據類型等稍有差異,而這些QL將是用戶與hive進行交互的直接語言!
查詢語言 |
HQL |
SQL |
數據存儲位置 |
HDFS |
Raw Device 或者 Local FS |
數據格式 |
用戶定義 |
系統決定 |
數據更新 |
不支持 |
支持 |
索引 |
無 |
有 |
執行 |
MapRedcue |
Executor |
執行延遲 |
高 |
低 |
可擴展性 |
高 |
低 |
數據規模 |
大 |
小 |
https://cwiki.apache.org/confluence/display/Hive/LanguageManual
DDL:
CREATE TABLE IF NOT EXISTS pokes (foo INT, bar STRING);
CREATE TABLE u_data ( userid INT, movieid INT, rating INT, unixtime STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS TEXTFILE;
SHOW TABLES;
ALTER TABLE pokes ADD COLUMNS (new_col INT);
DROP TABLE IF EXISTS pokes;
DML:
LOAD DATA LOCAL INPATH './examples/files/kv1.txt' OVERWRITE INTO TABLE pokes;
Load語句的使用注意Local和overwrite的選擇。
Sql query:
hive> FROM invites a INSERT OVERWRITE TABLE events SELECT a.bar, count(*) WHERE a.foo > 0 GROUP BY a.bar; hive> INSERT OVERWRITE TABLE events SELECT a.bar, count(*) FROM invites a WHERE a.foo > 0 GROUP BY a.bar; ROM invites a INSERT OVERWRITE TABLE events SELECT a.bar, count(*) WHERE a.foo > 0 GROUP BY a.bar; hive> INSERT OVERWRITE TABLE events SELECT a.bar, count(*) FROM invites a WHERE a.foo > 0 GROUP BY a.bar;
hive> FROM pokes t1 JOIN invites t2 ON (t1.bar = t2.bar) INSERT OVERWRITE TABLE events SELECT t1.bar, t1.foo, t2.foo;
Hive的查詢優化
載錄 http://www.open-open.com/lib/view/open1341214750402.html
join優化
Join查找操作的基本原則:應該將條目少的表/子查詢放在 Join 操作符的左邊。原因是在 Join 操作的 Reduce 階段,位於 Join 操作符左邊的表的內容會被加載進內存,將條目少的表放在左邊,可以有效減少發生內存溢出錯誤的幾率。
Join查找操作中如果存在多個join,且所有參與join的表中其參與join的key都相同,則會將所有的join合並到一個mapred程序中。
案例:
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1) 在一個mapre程序中執行join
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2) 在兩個mapred程序中執行join
Map join的關鍵在於join操作中的某個表的數據量很小,案例:
SELECT /*+ MAPJOIN(b) */ a.key, a.value
FROM a join b on a.key = b.key
Mapjoin 的限制是無法執行a FULL/RIGHT OUTER JOIN b,和map join相關的hive參數:hive.join.emit.interval hive.mapjoin.size.key hive.mapjoin.cache.numrows
由於join操作是在where操作之前執行,所以當你在執行join時,where條件並不能起到減少join數據的作用;案例:
SELECT a.val, b.val FROM a LEFT OUTER JOIN b ON (a.key=b.key)
WHERE a.ds='2009-07-07' AND b.ds='2009-07-07'
最好修改為:
SELECT a.val, b.val FROM a LEFT OUTER JOIN b
ON (a.key=b.key AND b.ds='2009-07-07' AND a.ds='2009-07-07')
在join操作的每一個mapred程序中,hive都會把出現在join語句中相對靠后的表的數據stream化,相對靠前的變的數據緩存在內存中。當然,也可以手動指定stream化的表:SELECT /*+ STREAMTABLE(a) */ a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)
group by 優化
Map端聚合,首先在map端進行初步聚合,最后在reduce端得出最終結果,相關參數:
· hive.map.aggr = true是否在 Map 端進行聚合,默認為 True
· hive.groupby.mapaggr.checkinterval = 100000在 Map 端進行聚合操作的條目數目
數據傾斜聚合優化,設置參數hive.groupby.skewindata = true,當選項設定為 true,生成的查詢計划會有兩個 MR Job。第一個 MR Job 中,Map 的輸出結果集合會隨機分布到 Reduce 中,每個 Reduce 做部分聚合操作,並輸出結果,這樣處理的結果是相同的 Group By Key 有可能被分發到不同的 Reduce 中,從而達到負載均衡的目的;第二個 MR Job 再根據預處理的數據結果按照 Group By Key 分布到 Reduce 中(這個過程可以保證相同的 Group By Key 被分布到同一個 Reduce 中),最后完成最終的聚合操作。
合並小文件
文件數目過多,會給 HDFS 帶來壓力,並且會影響處理效率,可以通過合並 Map 和 Reduce 的結果文件來消除這樣的影響:
· hive.merge.mapfiles = true是否和並 Map 輸出文件,默認為 True
· hive.merge.mapredfiles = false是否合並 Reduce 輸出文件,默認為 False
· hive.merge.size.per.task = 256*1000*1000合並文件的大小
Hive實現(not) in
通過left outer join進行查詢,(假設B表中包含另外的一個字段 key1
select a.key from a left outer join b on a.key=b.key where b.key1 is null
通過left semi join 實現 in
SELECT a.key, a.val FROM a LEFT SEMI JOIN b on (a.key = b.key)
Left semi join 的限制:join條件中右邊的表只能出現在join條件中。
排序優化
Order by 實現全局排序,一個reduce實現,效率低
Sort by 實現部分有序,單個reduce輸出的結果是有序的,效率高,通常和DISTRIBUTE BY關鍵字一起使用(DISTRIBUTE BY關鍵字 可以指定map 到 reduce端的分發key)
CLUSTER BY col1 等價於DISTRIBUTE BY col1 SORT BY col1
使用分區
Hive中的每個分區都對應hdfs上的一個目錄,分區列也不是表中的一個實際的字段,而是一個或者多個偽列,在表的數據文件中實際上並不保存分區列的信息與數據。Partition關鍵字中排在前面的為主分區(只有一個),后面的為副分區
靜態分區:靜態分區在加載數據和使用時都需要在sql語句中指定
案例:(stat_date='20120625',province='hunan')
動態分區:使用動態分區需要設置hive.exec.dynamic.partition參數值為true,默認值為false,在默認情況下,hive會假設主分區時靜態分區,副分區使用動態分區;如果想都使用動態分區,需要設置set hive.exec.dynamic.partition.mode=nostrick,默認為strick
案例:(stat_date='20120625',province)
Distinct 使用
Hive支持在group by時對同一列進行多次distinct操作,卻不支持在同一個語句中對多個列進行distinct操作。
Hql使用自定義的mapred腳本
注意事項:在使用自定義的mapred腳本時,關鍵字MAP REDUCE 是語句SELECT TRANSFORM ( ... )的語法轉換,並不意味着使用MAP關鍵字時會強制產生一個新的map過程,使用REDUCE關鍵字時會產生一個red過程。
自定義的mapred腳本可以是hql語句完成更為復雜的功能,但是性能比hql語句差了一些,應該盡量避免使用,如有可能,使用UDTF函數來替換自定義的mapred腳本
UDTF
UDTF將單一輸入行轉化為多個輸出行,並且在使用UDTF時,select語句中不能包含其他的列,UDTF不支持嵌套,也不支持group by 、sort by等語句。如果想避免上述限制,需要使用lateral view語法,案例:
select a.timestamp, get_json_object(a.appevents, '$.eventid'), get_json_object(a.appenvets, '$.eventname') from log a;
select a.timestamp, b.*
from log a lateral view json_tuple(a.appevent, 'eventid', 'eventname') b as f1, f2;
其中,get_json_object為UDF函數,json_tuple為UDTF函數。
UDTF函數在某些應用場景下可以大大提高hql語句的性能,如需要多次解析json或者xml數據的應用場景。
聚合函數count和sum
Count和sum函數可能是在hql語句中使用的最為頻繁的兩個聚合函數了,但是在hive中count函數在計算distinct value時支持加入條件過濾。