hive介紹
Hive通常意義上來說徘层,是把一個SQL轉(zhuǎn)化成一個分布式作業(yè),如MapReduce吻商,Spark或者Tez掏颊。無論Hive的底層執(zhí)行框架是MapReduce、Spark還是Tez艾帐,其原理基本都類似乌叶。
而目前,由于MapReduce穩(wěn)定柒爸,容錯性好准浴,大量數(shù)據(jù)情況下使用磁盤,能處理的數(shù)據(jù)量大捎稚,所以目前Hive的主流執(zhí)行框架是MapReduce乐横,但性能相比Spark和Tez也就較低。
Hive的基本組成
用戶接口:包括 CLI今野、JDBC/ODBC葡公、WebGUI。
CLI:command line interface条霜,命令行接口催什。
Hive WEB Interface(HWI):hive客戶端提供了一種通過網(wǎng)頁的方式訪問hive所提供的服務(wù)。
ThriftServers:提供JDBC和ODBC接入的能力,它用來進行可擴展且跨語言的服務(wù)的開發(fā)宰睡,hive集成了該服務(wù)蒲凶,能讓不同的編程語言調(diào)用hive的接口气筋。元數(shù)據(jù)存儲:通常是存儲在關(guān)系數(shù)據(jù)庫如 mysql , derby中。
-
語句轉(zhuǎn)換:解釋器旋圆、編譯器宠默、優(yōu)化器、執(zhí)行器灵巧。
基本組成 查詢語言搀矫。由于 SQL 被廣泛的應(yīng)用在數(shù)據(jù)倉庫中,因此孩等,專門針對 Hive 的特性設(shè)計了類 SQL 的查詢語言 HQL艾君。熟悉 SQL 開發(fā)的開發(fā)者可以很方便的使用 Hive 進行開發(fā)采够。
數(shù)據(jù)存儲位置肄方。Hive 是建立在Hadoop 之上的,所有 Hive 的數(shù)據(jù)都是存儲在HDFS 中的蹬癌。
數(shù)據(jù)格式权她。Hive 中沒有定義專門的數(shù)據(jù)格式,數(shù)據(jù)格式可以由用戶指定逝薪,用戶定義數(shù)據(jù)格式需要指定三個屬性:列分隔符(通常為空格隅要、”\t”、”\x001″)董济、行分隔符(”\n”)以及讀取文件數(shù)據(jù)的方法(Hive 中默認有三個文件格式 TextFile步清,SequenceFile 以及 RCFile)。由于在加載數(shù)據(jù)的過程中虏肾,不需要從用戶數(shù)據(jù)格式到 Hive 定義的數(shù)據(jù)格式的轉(zhuǎn)換廓啊,因此,Hive 在加載的過程中不會對數(shù)據(jù)本身進行任何修改封豪,而只是將數(shù)據(jù)內(nèi)容復(fù)制或者移動到相應(yīng)的 HDFS 目錄中谴轮。
數(shù)據(jù)更新。由于 Hive 是針對數(shù)據(jù)倉庫應(yīng)用設(shè)計的吹埠,而數(shù)據(jù)倉庫的內(nèi)容是讀多寫少的第步。因此,Hive 中不支持對數(shù)據(jù)的改寫和添加缘琅,所有的數(shù)據(jù)都是在加載的時候中確定好的粘都。
索引。之前已經(jīng)說過刷袍,Hive 在加載數(shù)據(jù)的過程中不會對數(shù)據(jù)進行任何處理驯杜,甚至不會對數(shù)據(jù)進行掃描,因此也沒有對數(shù)據(jù)中的某些 Key 建立索引做个。Hive 要訪問數(shù)據(jù)中滿足條件的特定值時鸽心,需要暴力掃描整個數(shù)據(jù)滚局,因此訪問延遲較高。由于 MapReduce 的引入顽频, Hive 可以并行訪問數(shù)據(jù)藤肢,因此即使沒有索引,對于大數(shù)據(jù)量的訪問糯景,Hive 仍然可以體現(xiàn)出優(yōu)勢嘁圈。
hive的四種表類型
- 內(nèi)部表(受控表),就是一般的表蟀淮,前面講到的表都是內(nèi)部表最住,當表定義被刪除的時候,表中的數(shù)據(jù)隨之一并被刪除怠惶。
- 外部表涨缚,數(shù)據(jù)存在與否和表的定義互不約束,僅僅只是表對hdfs上相應(yīng)文件的一個引用策治,當刪除表定義的時候脓魏,表中的數(shù)據(jù)依然存在。
創(chuàng)建外部表通惫,external是外部表的關(guān)鍵字茂翔,也是和內(nèi)部表有區(qū)別的地方
create external table tblName(colName colType...);
加載數(shù)據(jù)
alter table tblName set location 'hdfs_absolute_uri';
外部表還可以在創(chuàng)建表的時候指定數(shù)據(jù)的位置,引用當前位置的數(shù)據(jù)履腋。
create external table tblName(colName colType...) location 'hdfs_absolute_uri';
內(nèi)部表和外部表的轉(zhuǎn)換:
內(nèi)——>外
alter table tblName set tblproperties('EXTERNAL'='TRUE');
外——>內(nèi)
alter table tblName set tblproperties('EXTERNAL'='FALSE');
-
分區(qū)表珊燎,表對應(yīng)一個目錄,分區(qū)也對應(yīng)一個目錄遵湖,分區(qū)中的數(shù)據(jù)對應(yīng)文件悔政。
如何創(chuàng)建一張分區(qū)表?只需要在之前的創(chuàng)建表后面使用partition by加上分區(qū)字段就可以了奄侠,eg.
create table tblName (
id int comment 'ID',
name string comment 'name'
) partitioned by (dt date comment 'create time')
row format delimited
fields terminated by '\t';
向分區(qū)表中插入數(shù)據(jù)(要指定分區(qū))
load data local inpath linux_fs_path into table tblName partition(dt='2015-12-12');
多個分區(qū)如何創(chuàng)建卓箫?
和單分區(qū)表的創(chuàng)建類似:
create table tblName (
id int comment 'ID',
name string comment 'name'
) partitioned by (year int comment 'admission year', school string comment 'school name')
row format delimited
fields terminated by '\t';
同時也可以從hdfs上引用數(shù)據(jù):
alter table tblName partition(year='2015', school='crxy') set 'hdfs_absolute_uri';
注意:
必須得現(xiàn)有分區(qū),必須要使用hdfs絕對路徑。
-
桶表
表對應(yīng)目錄垄潮,桶對應(yīng)文件烹卒,桶表是對存入的數(shù)據(jù)按照某個字段進行哈希取值,值相同的放到同一個文件存儲弯洗。對于每一個表或者是分區(qū)旅急,Hive可以進一步組織成桶,也就是說桶是更為細粒度的數(shù)據(jù)范圍劃分牡整。Hive是針對某一列進行分桶藐吮。Hive采用對列值哈希,然后除以桶的個數(shù)求余的方式?jīng)Q定該條記錄存放在哪個桶中。分桶的好處是可以獲得更高的查詢處理效率谣辞。使取樣更高效迫摔。
創(chuàng)建 emp_bucket 表,字段如下泥从,按照 job 字段分成四個桶
create table bucketed_user(
id int,
name string
)
clustered by(id) sorted by(name) into 4 buckets
row format delimited fields terminated by '\t'
stored as textfile;
我們使用用戶id來確定如何劃分桶(Hive使用對值進行哈希并將結(jié)果除于桶的個數(shù)取余數(shù)的方式進行分桶)
如何加載數(shù)據(jù)句占?不能使用load data這種方式,需要從別的表來引用
insert into bucketed_user select * from user;
注意:在插入數(shù)據(jù)之前需要先設(shè)置開啟桶操作躯嫉,不然插入數(shù)據(jù)不會設(shè)置為桶!
set hive.enforce.bucketing=true;
當從桶表中進行查詢時纱烘,hive會根據(jù)分桶的字段進行計算分析出數(shù)據(jù)存放的桶中,然后直接到對應(yīng)的桶中去取數(shù)據(jù)祈餐,這樣做就很好的提高了效率擂啥。
Hive 表默認的倉庫路徑:user/hive/warehouse/tablename
Group By和Join
首先是Group By
例如我們有一條SQL語句:
INSERT INTO TABLE pageid_age_sum
SELECT pageid, age, count(1)
FROM pv_users
GROUP BY pageid, age;
把每個網(wǎng)頁的閱讀數(shù)按年齡進行分組統(tǒng)計。由于前面介紹了帆阳,MapReduce就是一個Group By的過程哺壶,這個SQL翻譯成MapReduce就是相對簡單的。
我們在Map端舱痘,每一個Map讀取一部分表的數(shù)據(jù)变骡,通常是64M或者128M离赫,然后按需要Group By的Key分發(fā)到Reduce端芭逝。經(jīng)過Shuffle Sort,每一個Key再在Reduce端進行聚合(這里是Count)渊胸,然后就輸出了最終的結(jié)果旬盯。值得一提的是,Distinct在實現(xiàn)原理上與Group By類似翎猛。當Group By遇上 Distinct……例如:
SELECT pageid, COUNT(DISTINCT userid) FROM page_view GROUP BY pageid
Hive 實現(xiàn)成MapReduce的原理如下:
也就是說Map分發(fā)到Reduce的時候胖翰,會使用pageid和userid作為聯(lián)合分發(fā)鍵,再去聚合(Count)切厘,輸出結(jié)果萨咳。
介紹了這么多原理,重點還是為了使用疫稿,為了適應(yīng)場景和業(yè)務(wù)培他,為了優(yōu)化。從原理上可以看出遗座,當遇到Group By的查詢時舀凛,會按Group By 鍵進行分發(fā)?如果鍵很多途蒋,撐爆了機器會怎么樣猛遍?
對于Impala,或Spark,為了快懊烤,key在內(nèi)存中梯醒,爆是經(jīng)常的。爆了就失敗了腌紧。對于Hive冤馏,Key在硬盤,本身就比Impala, Spark的處理能力大上幾萬倍寄啼。但……不幸的是逮光,硬盤也有可能爆。
當然墩划,硬盤速度也比內(nèi)存慢上不少涕刚,這也是Hive總是被吐槽的原因,場景不同乙帮,要明白自己使用的場景杜漠。當Group By Key大到連硬盤都能撐爆時……這個時候可能就需要優(yōu)化了。
Group By優(yōu)化通常有Map端數(shù)據(jù)聚合和傾斜數(shù)據(jù)分發(fā)兩種方式察净。Map端部分聚合驾茴,配置開關(guān)是hive.map.aggr
也就是執(zhí)行SQL前先執(zhí)行 set hive.map.aggr=true
;它的原理是Map端在發(fā)到Reduce端之前先部分聚合一下。來減少數(shù)據(jù)量氢卡。因為我們剛才已經(jīng)知道锈至,聚合操作是在Reduce端完成的,只要能有效的減少Reduce端收到的數(shù)據(jù)量译秦,就能有效的優(yōu)化聚合速度峡捡,避免爆機,快速拿到結(jié)果筑悴。
另外一種方式則是針對傾斜的key做兩道作業(yè)的聚合们拙。什么是傾斜的數(shù)據(jù)?比如某貓雙11交易阁吝,華為賣了1億臺砚婆,蘋果賣了10萬臺。華為就是典型的傾斜數(shù)據(jù)了突勇。如果要統(tǒng)計華為和蘋果装盯,會用兩個Reduce作Group By,一個處理1億臺与境,一個處理10萬臺验夯,那個1億臺的就是傾余。
由于按key分發(fā)摔刁,遇到傾斜數(shù)據(jù)怎么辦挥转?
可以使用hive.groupby.skewindata
選項,通過兩道MapReduce作業(yè)來處理。當選項設(shè)定為 true绑谣,生成的查詢計劃會有兩個 MR Job党窜。第一個 MR Job 中,Map 的輸出結(jié)果集合會隨機分布到Reduce 中借宵,每個 Reduce 做部分聚合操作幌衣,并輸出結(jié)果,這樣處理的結(jié)果是相同的 Group By Key有可能被分發(fā)到不同的 Reduce 中壤玫,從而達到負載均衡的目的豁护;第二個 MR Job 再根據(jù)預(yù)處理的數(shù)據(jù)結(jié)果按照 Group ByKey 分布到 Reduce 中(這個過程可以保證相同的 Group By Key 被分布到同一個 Reduce中),最后完成最終的聚合操作欲间。
第一道作業(yè):Map隨機分發(fā)楚里,按gby key部分聚合
第二道作業(yè):第一道作業(yè)結(jié)果Map傾斜的key分發(fā),按gbk key進行最終聚合
無論你使用Map端猎贴,或者兩道作業(yè)班缎。其原理都是通過部分聚合來來減少數(shù)據(jù)量。能不能部分聚合她渴,部分聚合能不能有效減少數(shù)據(jù)量达址,通常與UDAF,也就是聚合函數(shù)有關(guān)趁耗。也就是只對代數(shù)聚合函數(shù)有效沉唠,對整體聚合函數(shù)無效。
所謂代數(shù)聚合函數(shù)对粪,就是由部分結(jié)果可以匯總出整體結(jié)果的函數(shù)右冻,如count装蓬,sum著拭。 所謂整體聚合函數(shù),就是無法由部分結(jié)果匯總出整體結(jié)果的函數(shù)牍帚,如avg儡遮,mean。 比如暗赶,sum, count鄙币,知道部分結(jié)果可以加和得到最終結(jié)果。 而對于蹂随,mean十嘿,avg,知道部分數(shù)據(jù)的中位數(shù)或者平均數(shù)岳锁,是求不出整體數(shù)據(jù)的中位數(shù)和平均數(shù)的绩衷。
在遇到復(fù)雜邏輯的時候,還是要具體問題具體分析,根據(jù)系統(tǒng)的原理咳燕,優(yōu)化邏輯勿决。剛才說了,Hive最重要的是Group By和Join招盲,所以下面我們講Join.
JOIN
例如這樣一個查詢:
INSERT INTO TABLE pv_users
SELECT pv.pageid, u.age
FROM page_view pv JOIN user u ON (pv.userid = u.userid);
把訪問和用戶表進行關(guān)聯(lián)低缩,生成訪問用戶表。Hive的Join也是通過MapReduce來完成的曹货。
就上面的查詢咆繁,在MapReduce的Join的實現(xiàn)過程如下:
Map端會分別讀入各個表的一部分數(shù)據(jù),把這部分數(shù)據(jù)進行打標顶籽,例如pv表標1么介,user表標2.
Map讀取是分布式進行的。標完完后分發(fā)到Reduce端蜕衡,Reduce 端根據(jù)Join Key壤短,也就是關(guān)聯(lián)鍵進行分組。然后按打的標進行排序慨仿,也就是圖上的Shuffle Sort久脯。
在每一個Reduce分組中,Key為111的在一起镰吆,也就是一臺機器上帘撰。同時,pv表的數(shù)據(jù)在這臺機器的上端万皿,user表的數(shù)據(jù)在這臺機器的下端摧找。
這時候,Reduce把pv表的數(shù)據(jù)讀入到內(nèi)存里牢硅,然后逐條與硬盤上user表的數(shù)據(jù)做Join就可以了蹬耘。
從這個實現(xiàn)可以看出,我們在寫Hive Join的時候减余,應(yīng)該盡可能把小表(分布均勻的表)寫在左邊综苔,大表(或傾斜表)寫在右邊。這樣可以有效利用內(nèi)存和硬盤的關(guān)系位岔,增強Hive的處理能力如筛。
同時由于使用Join Key進行分發(fā), Hive也只支持等值Join抒抬,不支持非等值Join杨刨。由于Join和Group By一樣存在分發(fā),所以也同樣存在著傾斜的問題擦剑。所以Join也要對抗傾斜數(shù)據(jù)妖胀,提升查詢執(zhí)行性能可免。
通常,有一種執(zhí)行非匙鲈粒快的Join叫Map Join 浇借。
Map Join 優(yōu)化
手動的Map Join SQL如下:
INSERT INTO TABLE pv_users
SELECT /*+ MAPJOIN(pv) */ pv.pageid, u.age
FROM page_view pv JOIN user u
ON (pv.userid = u.userid);
還是剛才的例子,用Map Join執(zhí)行
Map Join通常只適用于一個大表和一個小表做關(guān)聯(lián)的場景怕品,例如事實表和維表的關(guān)聯(lián)妇垢。
原理如上圖,用戶可以手動指定哪個表是小表肉康,然后在客戶端把小表打成一個哈希表序列化文件的壓縮包闯估,通過分布式緩存均勻分發(fā)到作業(yè)執(zhí)行的每一個結(jié)點上。然后在結(jié)點上進行解壓吼和,在內(nèi)存中完成關(guān)聯(lián)涨薪。
Map Join全過程不會使用Reduce,非常均勻炫乓,不會存在數(shù)據(jù)傾斜問題刚夺。默認情況下,小表不應(yīng)該超過25M末捣。在實際使用過程中侠姑,手動判斷是不是應(yīng)該用Map Join太麻煩了,而且小表可能來自于子查詢的結(jié)果箩做。
Hive有一種稍微復(fù)雜一點的機制莽红,叫Auto Map Join
還記得原理中提到的物理優(yōu)化器?Physical Optimizer么邦邦?它的其中一個功能就是把Join優(yōu)化成Auto Map Join
圖上左邊是優(yōu)化前的安吁,右邊是優(yōu)化后的
優(yōu)化過程是把Join作業(yè)前面加上一個條件選擇器ConditionalTask和一個分支。左邊的分支是MapJoin燃辖,右邊的分支是Common Join(Reduce Join)
看看左邊的分支是不是和我們上上一張圖很像鬼店?
這個時候,我們在執(zhí)行的時候郭赐,就由這個Conditional Task 進行實時路徑選擇薪韩,遇到小于25兆走左邊,大于25兆走右邊捌锭。
在比較新版的Hive中,Auto Mapjoin是默認開啟的罗捎。如果沒有開啟观谦,可以使用一個開關(guān), set hive.auto.convert.join=true
開啟桨菜。
當然豁状,Join也會遇到和上面的Group By一樣的傾斜問題捉偏。
Hive 也可以通過像Group By一樣兩道作業(yè)的模式單獨處理一行或者多行傾斜的數(shù)據(jù)。
hive 中設(shè)定
set hive.optimize.skewjoin = true;
set hive.skewjoin.key = skew_key_threshold (default = 100000)
其原理是就在Reduce Join過程泻红,把超過十萬條的傾斜鍵的行寫到文件里夭禽,回頭再起一道Join單行的Map Join作業(yè)來單獨收拾它們。最后把結(jié)果取并集就是了谊路。如上圖所示讹躯。