Hive作為大數(shù)據(jù)領(lǐng)域常見的數(shù)據(jù)倉庫組件仍劈,在設(shè)計和開發(fā)階段需要注意效率。影響Hive效率的不僅僅是數(shù)據(jù)量過大、數(shù)據(jù)傾斜、job(小文件過多)或者磁盤I/O過多、MapReduce分配不合理等因素都會對Hive的效率有影響奔浅。對Hive的調(diào)優(yōu)可以從架構(gòu)優(yōu)化、參數(shù)優(yōu)化以及Hive SQL優(yōu)化三個方面考慮诗良。
一汹桦、架構(gòu)優(yōu)化
1、執(zhí)行引擎
Hive支持多種執(zhí)行引擎鉴裹,例如:MR舞骆、Tez、Spark等径荔《角荩可以通過hive-site.xml文件中的hive.execution.engine屬性配置。
2总处、優(yōu)化器
與關(guān)系型數(shù)據(jù)庫類型狈惫,Hiv在真正執(zhí)行的時候,會先通過解釋器生成AST抽象語法樹鹦马,然后再通過編譯器生成邏輯執(zhí)行計劃胧谈,再通過優(yōu)化器進(jìn)行優(yōu)化忆肾,優(yōu)化后通過執(zhí)行器生成物理執(zhí)行計劃。而Hive有兩種優(yōu)化器:
Vectorize(矢量化優(yōu)化器)和Cost-Based Optimization(CBO成本優(yōu)化器)
1)矢量化查詢優(yōu)化(向量化優(yōu)化器)
矢量化查詢執(zhí)行通過一次批量執(zhí)行1024行菱肖,而不是一行一行來提高掃描客冈、聚合、過濾器和鏈接等操作的性能稳强,這個功能明顯縮短查詢執(zhí)行時間场仲。
-- 默認(rèn) false
SET hive.vectorized.execution.enabled = true;
-- 默認(rèn) false
SET hive.vectorized.execution.reduce.enabled = true;
備注:
● 要使用矢量化查詢執(zhí)行,必須用ORC格式存儲數(shù)據(jù)
● 要求執(zhí)行引擎為Tez
2)成本優(yōu)化器
Hive的CBO是基于Apache Calcite的退疫,Hive的CBO通過查詢成本(有analyze收集的統(tǒng)計信息)會生成有效率的執(zhí)行計劃燎窘,最終會較少執(zhí)行的時間和資源利用,使用CBO的配置如下:
--從 v0.14.0默認(rèn)為true
SET hive.cbo.enable=true;
-- 默認(rèn)false
SET hive.compute.query.using.stats=true;
-- 默認(rèn)false
SET hive.stats.fetch.column.stats=true;
-- 默認(rèn)true
SET hive.stats.fetch.partition.stats=true;
定期執(zhí)行表(分析的命令:analyze)的分析蹄咖,分析后的數(shù)據(jù)放在元數(shù)據(jù)庫中。
低版本情況下付鹿,小表在前的確效率高澜汤,高版本優(yōu)化器已經(jīng)做了優(yōu)化。是因為小表的數(shù)據(jù)可能會放在內(nèi)存里面舵匾,達(dá)標(biāo)的數(shù)據(jù)內(nèi)存存不下就會導(dǎo)致效率低俊抵。
3、分區(qū)表
對于一個比較大的表坐梯,將其設(shè)計為分區(qū)表徽诲,可以提升查詢的性能,對于一個特定分區(qū)的查詢吵血,只會加載對應(yīng)分區(qū)路徑的數(shù)據(jù)文件谎替,所以執(zhí)行速度比較快。
分區(qū)字段的選擇蹋辅,避免層級較深的分區(qū)钱贯,否則會造成太多的子文件夾,常見的分區(qū)字段:
日期或時間侦另。如year秩命、month、day或者h(yuǎn)our褒傅,當(dāng)表中存在時間或者日期字段時弃锐。
地理問題。如:國家殿托、省份霹菊、城市等。
業(yè)務(wù)邏輯碌尔。如:部門浇辜、銷售區(qū)域券敌、客戶等等。
4柳洋、分桶表
與分區(qū)表類似待诅,分桶表的組織方式是將HDFS上的文件分割成多個文件。
分桶可以加快數(shù)據(jù)采樣熊镣,也可以提高join的性能卑雁,join的字段是分桶字段,因為分桶可以確保某一個key對應(yīng)的數(shù)據(jù)在一個特定的桶內(nèi)(文件)绪囱,巧妙的選擇分桶字段测蹲,可以大幅度提升join性能。
通常情況下鬼吵,分桶字段可以選擇經(jīng)常用過濾操作或者join操作的字段扣甲。
5、文件格式
在Hive SQL的創(chuàng)表語句中齿椅,可以使用 stored as... 指定表的存儲格式琉挖。Hive表支持的存儲格式有TextFile、SequenceFile涣脚、RCFile示辈、ORC、Parquet等遣蚀。
存儲格式一般需要根據(jù)業(yè)務(wù)進(jìn)行選擇矾麻,生產(chǎn)環(huán)境中絕大多數(shù)表都采用TextFile、ORC芭梯、Parquet存儲格式之一险耀。
- TextFile是最簡單的存儲格式,它是純文本記錄粥帚,也是Hive默認(rèn)格式胰耗。其磁盤開銷大,查詢效率低芒涡,更多的是作為跳板來使用柴灯。RCFile、ORC费尽、Parquet等格式的表都不能由文件直接導(dǎo)入數(shù)據(jù)赠群,必須由TextFile來做中轉(zhuǎn)。
- Parquet和ORC都是開源列式存儲格式旱幼。列式存儲比起傳統(tǒng)的行式存儲更適合批量OLAP查詢查描,并且也支持更好的壓縮和編碼。選擇Parquet的原因主要是它支持Impala查詢引擎,并且對update冬三、delete和事務(wù)性操作需求很低匀油。
6、數(shù)據(jù)壓縮
壓縮技術(shù)可以減少map與reduce之間的數(shù)據(jù)傳輸勾笆,從而可以提升查詢性能敌蚜,關(guān)于壓縮的配置可以在hive命令行中或者h(yuǎn)ive-site.xml文件中進(jìn)行配置。
-- 默認(rèn)式false
SET hive.exec.compress.intermediate=true
開啟壓縮后窝爪,可以選擇下面的壓縮格式:
關(guān)于壓縮的編碼器可以通過mapred-site.xml弛车,hive-site.xml進(jìn)行配置,也可以通過命令行進(jìn)行配置蒲每,如:
-- 中間結(jié)果壓縮
SET hive.intermediate.compression.codec=org.apache.hadoop.io.compress.SnappyCodec;
-- 輸出結(jié)果壓縮
SET hive.exec.compress.output=true;
SET mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodc
二纷跛、參數(shù)優(yōu)化
1、本地模式
當(dāng)Hive處理的數(shù)據(jù)量較小的時候邀杏,啟動分布式處理數(shù)據(jù)就會顯得浪費贫奠,因為可能啟動時間比處理數(shù)據(jù)時間還要長,Hive支持將作業(yè)動態(tài)的轉(zhuǎn)為本地模式望蜡,需要使用下面的配置:
-- 默認(rèn) false
SET hive.exec.mode.local.auto=true;
-- 默認(rèn)128M
SET hive.exec.mode.local.auto.inputbytes.max=50000000;
-- 默認(rèn) 4
SET hive.exec.mode.local.auto.input.files.max=5;
一個作業(yè)只要滿足下面的條件叮阅,會啟動本地模式:
輸入文件的大小小于 hive.exec.mode.local.auto.inputbytes.max 配置的大小泣特;
map任務(wù)的數(shù)量小于 hive.exec.mode.local.auto.input.files.max 配置的大小挑随;
reduce任務(wù)的數(shù)量是1或者0状您。
2、嚴(yán)格模式
所謂嚴(yán)格模式就是不允許執(zhí)行3種有風(fēng)險的HQL語句:
查詢分區(qū)表的時候不限定分區(qū)列的語句
兩個表join產(chǎn)生了笛卡爾積
用order by 來排序兜挨,但是沒有指定limit
要開啟嚴(yán)格模式膏孟,需要將參數(shù)要開啟嚴(yán)格模式,需要將參數(shù) hive.mapred.mode 設(shè)為strict(缺省值)拌汇。
該參數(shù)可以不在參數(shù)文件中定義柒桑,在執(zhí)行SQL之前設(shè)置(set hive.mapred.mode=nostrict),即在當(dāng)前SQL不是嚴(yán)格模式噪舀。
3魁淳、JVM重用
默認(rèn)情況下,Hadoop會為一個map或者reduce啟動一個JVM与倡,這樣可以并行執(zhí)行map和reduce界逛。當(dāng)map或者reduce是那種僅運行幾秒鐘的輕量級作業(yè)時,JVM啟動進(jìn)程所耗費的時間會比作業(yè)執(zhí)行的時間還要長纺座。Hadoop可以重用JVM息拜,通過共享JVM以串行而非并行的方式運行map或者reduce。
JVM的重用適用于同一個作業(yè)的map和reduce,對于不同作業(yè)的task不能夠共享JVM少欺。如果要開啟JVM重用喳瓣,需要配置一個作業(yè)最大task數(shù)量,默認(rèn)值為1赞别,如果設(shè)置為-1畏陕,則表示不限制:
-- 代表同一個MR job中順序執(zhí)行的5個task重復(fù)使用一個JVM,減少啟動和關(guān)閉的開銷
SET mapreduce.job.jvm.numtasks=5;
這個功能的缺點是氯庆,開啟JVM重用將一直占用使用到的task插槽蹭秋,以便進(jìn)行重用,直到任務(wù)完成后才能釋放堤撵。如果某個“不平衡的”job中有某幾個reduce task執(zhí)行的時間 要比其他Reduce task消耗的時間多的多的話仁讨,那么保留的插槽就會一直空閑著卻無 法被其他的job使用,直到所有的task都結(jié)束了才會釋放实昨。
4洞豁、并行執(zhí)行
Hive的查詢通常會被轉(zhuǎn)換成一系列的stage,這些stage之間并不是一直相互依賴的荒给,可以并行執(zhí)行這些stage丈挟,通過下面的方式進(jìn)行配置:
SET hive.exec.parallel=true; -- 默認(rèn)false
SET hive.exec.parallel.thread.number=16; -- 默認(rèn)8
并行執(zhí)行可以增加集群資源的利用率,如果集群的資源使用率已經(jīng)很高了志电,那么并行執(zhí)行的效果不會很明顯曙咽。
5、推測執(zhí)行
在分布式集群環(huán)境下挑辆,因為程序Bug例朱、負(fù)載不均衡、資源分布不均勻等原因鱼蝉,會造成同一個作業(yè)的多個任務(wù)之間運行速度不一致洒嗤,有些任務(wù)的運行速度可能明顯慢于其他任務(wù)(比如一個作業(yè)的某個任務(wù)進(jìn)度只有50%,而其他所有任務(wù)已經(jīng)運行完成)魁亦,則這些任務(wù)會拖慢作業(yè)的整體執(zhí)行進(jìn)度渔隶。
為了避免這種情況發(fā)生,Hadoop采用了推測執(zhí)行機制洁奈,它根據(jù)一定的規(guī)則推測出“拖后腿”的任務(wù)间唉,并為這樣的任務(wù)啟動一個備份任務(wù),讓該任務(wù)與原始任務(wù)同時處理同一份數(shù)據(jù)利术,并最終選用最先成功運行完成任務(wù)的計算結(jié)果作為最終結(jié)果终吼。
set mapreduce.map.speculative=true
set mapreduce.reduce.speculative=true
set hive.mapred.reduce.tasks.speculative.execution=true
6、合并小文件
- 在map執(zhí)行前合并小文件氯哮,減少map數(shù)
-- 缺省參數(shù)
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
- 在Map-Reduce的任務(wù)結(jié)束時合并小文件
-- 在 map-only 任務(wù)結(jié)束時合并小文件际跪,默認(rèn)true
SET hive.merge.mapfiles = true;
-- 在 map-reduce 任務(wù)結(jié)束時合并小文件商佛,默認(rèn)false
SET hive.merge.mapredfiles = true;
-- 合并文件的大小,默認(rèn)256M
SET hive.merge.size.per.task = 268435456;
-- 當(dāng)輸出文件的平均大小小于該值時姆打,啟動一個獨立的map-reduce任務(wù)進(jìn)行文件merge
SET hive.merge.smallfiles.avgsize = 16777216;
7良姆、Fetch模式
Fetch模式是指Hive中對某些情況的查詢可以不必使用MR計算,select col1, col2 from tab;
可以簡單地讀取表對應(yīng)的存儲目錄下的文件幔戏,然后輸出查詢結(jié)果到控制臺玛追,在開啟Fetch模式之后,在全局查找闲延、字段查找痊剖、limit查找等都不啟動MR。
-- Default Value: minimal in Hive 0.10.0 through 0.13.1, more in Hive 0.14.0 and later
set hive.fetch.task.conversion=more
三垒玲、HiveSQL優(yōu)化
1陆馁、列裁剪和分區(qū)裁剪
最基本的操作。所謂列裁剪就是在查詢時只讀取需要的列合愈,分區(qū)裁剪就是讀取需要的分區(qū)叮贩。以用戶維度表為例:
SELECT user_id, user_name
FROM dim_user_dd_f
WHERE dt = '2021-10-28'
and user_level = 1
當(dāng)列很多或者數(shù)據(jù)量很大時,如果select *或者不指定分區(qū)佛析,全列掃描和全表掃描效率都很低益老。
Hive中與列裁剪優(yōu)化相關(guān)的配置項是hive.optimize.cp,與分區(qū)裁剪優(yōu)化相關(guān)的則是hive.optimize.pruner寸莫,默認(rèn)都是true捺萌。在HiveSQL解析階段對應(yīng)的則是ColumnPruner邏輯優(yōu)化器。
2膘茎、謂詞下推
謂詞下推就是將SQL語句中的where謂詞邏輯都盡可能提前執(zhí)行互婿,減少下游處理的數(shù)據(jù)量。
例如辽狈,如下Hive SQL語句:
SELECT a.uid, a.event_type, b.topic_id, b.title
FROM calendar_record_log a
LEFT OUTER JOIN
(
SELECT uid, topic_id, title
FROM forum_topic
WHERE pt_date = 20190224
AND length(content) >= 100
) b ON a.uid = b.uid
WHERE a.pt_date = 20190224
AND status = 0
對forum_topic做過濾的where語句寫在子查詢內(nèi)部,而不是外部呛牲。Hive中有謂詞下推優(yōu)化的配置項hive.optimize.ppd刮萌,默認(rèn)值true,與它對應(yīng)的邏輯優(yōu)化器是PredicatePushDown娘扩。該優(yōu)化器就是將OperatorTree中的FilterOperator向上提着茸,見下圖。
3琐旁、sort by 代替 order by
HiveSQL中的order by 就是將結(jié)果按照某個字段進(jìn)行全局排序涮阔,這會導(dǎo)致所有map端數(shù)據(jù)都進(jìn)入一個reducer中,在數(shù)據(jù)量大時可能會長時間計算不完灰殴。
如果使用sort by敬特,那么還是會視情況啟動多個reducer進(jìn)行排序,并且保證每個reducer內(nèi)局部有序。為了控制map端數(shù)據(jù)分配到reducer的key伟阔,往往還要配合distribute by一同使用辣之。如果不加distribute by的話,map端數(shù)據(jù)就會隨機分配到reducer皱炉。
舉個例子怀估,加入要以UID為key,以上傳時間倒序合搅,記錄類型倒序輸出記錄數(shù)據(jù):
4多搀、group by 代替 count(distinct)
當(dāng)要統(tǒng)計某一列的去重數(shù)時,如果數(shù)據(jù)量很大灾部,count(distinct)就會非常慢康铭,原因與order by類似,count(distinct)邏輯只會有很少的reducer來處理梳猪。這時可以用group by來改寫:
但是這樣寫會啟動兩個MR job(單純distinct只會啟動一個)麻削,所以要確保數(shù)據(jù)量大到啟動job的overHead遠(yuǎn)小于計算耗時,才考慮這種方法春弥。當(dāng)數(shù)據(jù)集很小或者key的傾斜比較明顯時呛哟,group by還可能會比distinct慢。
5匿沛、group by配置調(diào)整---map端預(yù)聚合
group by時扫责,如果先起一個combine在map端做部分預(yù)聚合,可以有效減少shuffle數(shù)據(jù)量逃呼。
-- 默認(rèn)為true
set hive.map.aggr = true;
Map端進(jìn)行聚合操作的條目數(shù)
set hive.groupby.mapaggr.checkinterval = 100000;
通過 hive.groupby.mapaggr.checkinterval 參數(shù)也可以設(shè)置map端預(yù)聚合的行 數(shù)閾值鳖孤,超過該值就會分拆job,默認(rèn)值10W抡笼。