在企業(yè)中使用Hive構(gòu)建離線數(shù)倉是一種十分普遍的方案赏壹。盡管Hive的使用場景是通過批處理的方式處理大數(shù)據(jù),通常對處理時(shí)間不敏感七兜。但是在資源有限的情況下,我們需要關(guān)注Hive的性能調(diào)優(yōu)枯冈,從而方便數(shù)據(jù)的快速產(chǎn)出。同時(shí)办悟,關(guān)于Hive的性能調(diào)優(yōu)尘奏,也是面試中比較常見的問題,因此掌握Hive性能調(diào)優(yōu)的一些方法病蛉,不僅能夠在工作中提升效率而且還可以在面試中脫穎而出炫加。本文會(huì)通過四個(gè)方面介紹Hive性能調(diào)優(yōu),主要包括:
性能調(diào)優(yōu)的工具
設(shè)計(jì)優(yōu)化
數(shù)據(jù)存儲(chǔ)優(yōu)化
作業(yè)優(yōu)化
性能調(diào)優(yōu)的工具
HQL提供了兩個(gè)查看查詢性能的工具:explain與analyze铺然,除此之外Hive的日志也提供了非常詳細(xì)的信息琢感,方便查看執(zhí)行性能和報(bào)錯(cuò)排查。
善用explain語句
explain語句是查看執(zhí)行計(jì)劃經(jīng)常使用的一個(gè)工具探熔,可以使用該語句分析查詢執(zhí)行計(jì)劃,具體使用語法如下:
EXPLAIN [FORMATTED|EXTENDED|DEPENDENCY|AUTHORIZATION] hql_query
上面的執(zhí)行語句中烘挫,有4個(gè)可選的關(guān)鍵字诀艰,其具體含義如下:
- FORMATTED:對執(zhí)行計(jì)劃進(jìn)行格式化,返回JSON格式的執(zhí)行計(jì)劃
- EXTENDED:提供一些額外的信息饮六,比如文件的路徑信息
- DEPENDENCY:以JSON格式返回查詢所依賴的表和分區(qū)的列表其垄,從Hive0.10開始使用,如下圖
- AUTHORIZATION:列出需要被授權(quán)的條目卤橄,包括輸入與輸出绿满,從Hive0.14開始使用,如下圖
一個(gè)典型的查詢執(zhí)行計(jì)劃主要包括三部分,具體如下:
- Abstract Syntax Tree (AST):抽象語法樹窟扑,Hive使用一個(gè)稱之為antlr的解析生成器喇颁,可以自動(dòng)地將HQL生成為抽象語法樹
- Stage Dependencies:會(huì)列出運(yùn)行查詢所有的依賴以及stage的數(shù)量
- Stage Plans:包含了非常重要的信息,比如運(yùn)行作業(yè)時(shí)的operator 和sort orders
舉個(gè)栗子
假設(shè)有一張表:
CREATE TABLE employee_partitioned
(
name string,
work_place ARRAY<string>,
gender_age STRUCT<gender:string,age:int>,
skills_score MAP<string,int>,
depart_title MAP<STRING,ARRAY<STRING>>
)
PARTITIONED BY (Year INT, Month INT)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '|'
COLLECTION ITEMS TERMINATED BY ','
MAP KEYS TERMINATED BY ':';
查看執(zhí)行計(jì)劃:
EXPLAIN
SELECT gender_age.gender,
count(*)
FROM employee_partitioned
WHERE YEAR=2020
GROUP BY gender_age.gender
LIMIT 2;
執(zhí)行計(jì)劃概覽:
如上圖:Map/Reduce operator tree是抽象語法樹AST部分嚎货;STAGE
DEPENDENCIES包括三個(gè)階段:Stage-0 橘霎、Stage-1及Stage-2,其中Stage-0 是root stage殖属,即Stage-1與Stage-2依賴于Stage-0姐叁;STAGE PLANS部分,Stage-1與Stage2都包含一個(gè)Map Operator Tree和一個(gè)Reduce Operator Tree,Stage-0不包含map和reduce外潜,僅僅是一個(gè)fetch數(shù)據(jù)的操作原环。
執(zhí)行計(jì)劃詳細(xì)信息:
STAGE DEPENDENCIES:
Stage-1 is a root stage
Stage-2 depends on stages: Stage-1
Stage-0 depends on stages: Stage-2
STAGE PLANS:
Stage: Stage-1
Map Reduce
Map Operator Tree:
TableScan
alias: employee_partitioned
filterExpr: (year = 2020) (type: boolean)
Statistics: Num rows: 1 Data size: 227 Basic stats: PARTIAL Column stats: NONE
Select Operator
expressions: gender_age (type: struct<gender:string,age:int>)
outputColumnNames: gender_age
Statistics: Num rows: 1 Data size: 227 Basic stats: PARTIAL Column stats: NONE
Reduce Output Operator
key expressions: gender_age.gender (type: string)
sort order: +
Map-reduce partition columns: rand() (type: double)
Statistics: Num rows: 1 Data size: 227 Basic stats: PARTIAL Column stats: NONE
Reduce Operator Tree:
Group By Operator
aggregations: count()
keys: KEY._col0 (type: string)
mode: partial1
outputColumnNames: _col0, _col1
Statistics: Num rows: 1 Data size: 227 Basic stats: COMPLETE Column stats: NONE
File Output Operator
compressed: false
table:
input format: org.apache.hadoop.mapred.SequenceFileInputFormat
output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
serde: org.apache.hadoop.hive.serde2.lazybinary.LazyBinarySerDe
Stage: Stage-2
Map Reduce
Map Operator Tree:
TableScan
Reduce Output Operator
key expressions: _col0 (type: string)
sort order: +
Map-reduce partition columns: _col0 (type: string)
Statistics: Num rows: 1 Data size: 227 Basic stats: COMPLETE Column stats: NONE
value expressions: _col1 (type: bigint)
Reduce Operator Tree:
Group By Operator
aggregations: count(VALUE._col0)
keys: KEY._col0 (type: string)
mode: final
outputColumnNames: _col0, _col1
Statistics: Num rows: 1 Data size: 227 Basic stats: COMPLETE Column stats: NONE
Limit
Number of rows: 2
Statistics: Num rows: 1 Data size: 227 Basic stats: COMPLETE Column stats: NONE
File Output Operator
compressed: false
Statistics: Num rows: 1 Data size: 227 Basic stats: COMPLETE Column stats: NONE
table:
input format: org.apache.hadoop.mapred.TextInputFormat
output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
Stage: Stage-0
Fetch Operator
limit: 2
Processor Tree:
ListSink
巧用analyze語句
analyze語句可以收集一些詳細(xì)的統(tǒng)計(jì)信息,比如表的行數(shù)处窥、文件數(shù)嘱吗、數(shù)據(jù)的大小等信息。這些統(tǒng)計(jì)信息作為元數(shù)據(jù)存儲(chǔ)在hive的元數(shù)據(jù)庫中碧库。Hive支持表柜与、分區(qū)和列級別的統(tǒng)計(jì)(與Impala類似),這些信息作為Hive基于成本優(yōu)化策略(Cost-Based Optimizer (CBO))的輸入,該優(yōu)化器的主要作用是選擇耗費(fèi)最小系統(tǒng)資源的查詢計(jì)劃嵌灰。其實(shí)弄匕,在Hive3.2.0版本中,可以自動(dòng)收集這些統(tǒng)計(jì)信息沽瞭,當(dāng)然也可以通過analyze語句進(jìn)行手動(dòng)統(tǒng)計(jì)表迁匠、分區(qū)或者字段的信息。具體的使用方式如下:
- 1.收集表的統(tǒng)計(jì)信息(非分區(qū)表)驹溃,當(dāng)指定NOSCAN關(guān)鍵字時(shí)城丧,會(huì)忽略掃描文件內(nèi)容,僅僅統(tǒng)計(jì)文件的數(shù)量與大小豌鹤,速度會(huì)比較快
-- 不使用NOSCAN關(guān)鍵字
hive> ANALYZE TABLE user_behavior COMPUTE STATISTICS;
...
Table default.user_behavior stats: [numFiles=1, numRows=10, totalSize=229, rawDataSize=219]
Time taken: 23.504 seconds
-- 使用NOSCAN關(guān)鍵字
hive> ANALYZE TABLE user_behavior COMPUTE STATISTICS NOSCAN;
Table default.user_behavior stats: [numFiles=1, numRows=10, totalSize=229, rawDataSize=219]
Time taken: 0.309 seconds
- 2.收集分區(qū)表的統(tǒng)計(jì)信息
-- 收集具體分區(qū)的統(tǒng)計(jì)信息
hive> ANALYZE TABLE employee_partitioned PARTITION(year=2020, month=06) COMPUTE STATISTICS;
...
Partition default.employee_partitioned{year=2020, month=06} stats: [numFiles=1, numRows=0, totalSize=227, rawDataSize=0]
Time taken: 19.283 seconds
-- 收集所有分區(qū)的統(tǒng)計(jì)信息
hive> ANALYZE TABLE employee_partitioned PARTITION(year, month) COMPUTE STATISTICS;
...
Partition default.employee_partitioned{year=2020, month=06} stats: [numFiles=1, numRows=0, totalSize=227, rawDataSize=0]
Time taken: 17.528 seconds
- 3.收集表的某個(gè)字段的統(tǒng)計(jì)信息
hive> ANALYZE TABLE user_behavior COMPUTE STATISTICS FOR COLUMNS user_id ;
尖叫提示:
可以通過設(shè)置:SET hive.stats.autogather=true亡哄,進(jìn)行自動(dòng)收集統(tǒng)計(jì)信息,對于INSERT OVERWRITE/INTO操作的表或者分區(qū)布疙,可以自動(dòng)收集統(tǒng)計(jì)信息蚊惯。值得注意的是,LOAD操作不能夠自動(dòng)收集統(tǒng)計(jì)信息
一旦這些統(tǒng)計(jì)信息收集完畢灵临,可以通過DESCRIBE EXTENDED/FORMATTED語句查詢統(tǒng)計(jì)信息截型,具體使用如下:
-- 查看一個(gè)分區(qū)的統(tǒng)計(jì)信息
hive> DESCRIBE FORMATTED employee_partitioned PARTITION(year=2020, month=06);
...
Partition Parameters:
COLUMN_STATS_ACCURATE true
numFiles 1
numRows 0
rawDataSize 0
totalSize 227
transient_lastDdlTime 1591437967
...
-- 查看一張表的統(tǒng)計(jì)信息
hive> DESCRIBE FORMATTED employee_partitioned;
...
Table Parameters:
numPartitions 1
transient_lastDdlTime 1591431482
...
-- 查看某列的統(tǒng)計(jì)信息
hive> DESCRIBE FORMATTED user_behavior.user_id;
常用日志分析
日志提供了job運(yùn)行的詳細(xì)信息,通過查看日志信息儒溉,可以分析出導(dǎo)致作業(yè)執(zhí)行瓶頸的問題宦焦,主要包括兩種類型的日志:系統(tǒng)日志和作業(yè)日志。
系統(tǒng)日志包含了Hive運(yùn)行時(shí)的狀態(tài)等信息顿涣,可以通過{HIVE_HOME}/conf/hive-log4j.properties文件進(jìn)行配置波闹,主要的配置選項(xiàng)有:
hive.root.logger=WARN,DRFA ## 日志級別
hive.log.dir=/tmp/${user.name} ## 日志路徑
hive.log.file=hive.log ## 日志名稱
也可以通過Hive cli命令行設(shè)置日志級別:$hive --hiveconf hive.root.logger=DEBUG,console
這種方式只能在當(dāng)前會(huì)話生效。
作業(yè)日志所包含的作業(yè)信息通常是由YARN管理的涛碑,可以通過yarn logs -applicationId <application_id>
命令查看作業(yè)日志舔痪。
設(shè)計(jì)優(yōu)化
分區(qū)表
對于一張比較大的表,將其設(shè)計(jì)成分區(qū)表可以提升查詢的性能锌唾,對于一個(gè)特定分區(qū)的查詢锄码,只會(huì)加載對應(yīng)分區(qū)路徑的文件數(shù)據(jù)夺英,所以執(zhí)行速度會(huì)比較快。值得注意的是滋捶,分區(qū)字段的選擇是影響查詢性能的重要因素痛悯,盡量避免層級較深的分區(qū),這樣會(huì)造成太多的子文件夾重窟。一些常見的分區(qū)字段可以是:
- 日期或者時(shí)間
比如year载萌、month、day或者h(yuǎn)our巡扇,當(dāng)表中存在時(shí)間或者日期字段時(shí)扭仁,可以使用些字段。
- 地理位置
比如國家厅翔、省份乖坠、城市等
- 業(yè)務(wù)邏輯
比如部門、銷售區(qū)域刀闷、客戶等等
分桶表
與分區(qū)表類似熊泵,分桶表的組織方式是將HDFS上的文件分割成多個(gè)文件。分桶可以加快數(shù)據(jù)采樣甸昏,也可以提升join的性能(join的字段是分桶字段)顽分,因?yàn)榉滞翱梢源_保某個(gè)key對應(yīng)的數(shù)據(jù)在一個(gè)特定的桶內(nèi)(文件),所以巧妙地選擇分桶字段可以大幅度提升join的性能施蜜。通常情況下卒蘸,分桶字段可以選擇經(jīng)常用在過濾操作或者join操作的字段。
索引
創(chuàng)建索引是關(guān)系型數(shù)據(jù)庫性能調(diào)優(yōu)的常見手段翻默,在Hive中也不例外缸沃。Hive從0.7版本開始支持索引,使用索引相比全表掃描而言冰蘑,是一種比較廉價(jià)的操作,Hive中創(chuàng)建索引的方式如下:
CREATE INDEX idx_user_id_user_behavior
ON TABLE user_behavior (user_id)
AS 'COMPACT'
WITH DEFERRED REBUILD;
上面創(chuàng)建的是COMPACT索引村缸,存儲(chǔ)的是索引列與其對應(yīng)的block id的pair對祠肥。除了此種索引外,Hive還支持位圖索引(BITMAP),使用方式如下:
CREATE INDEX idx_behavior_user_behavior
ON TABLE user_behavior (behavior)
AS 'BITMAP'
WITH DEFERRED REBUILD;
上面創(chuàng)建的索引時(shí)梯皿,使用了WITH DEFERRED REBUILD
選項(xiàng)仇箱,該選項(xiàng)可以避免索引立即被創(chuàng)建,當(dāng)建立索引時(shí)东羹,可以使用LTER...REBUILD
命令(見下面的示例)剂桥,值得注意的是:當(dāng)基表(被創(chuàng)建索引的表)發(fā)生變化時(shí),該命令需要被再次執(zhí)行以便更新索引到最新的狀態(tài)属提。
ALTER INDEX idx_user_id_user_behavior ON user_behavior REBUILD;
一旦索引創(chuàng)建成功权逗,會(huì)生成一張索引表美尸,表的名稱格式為:數(shù)據(jù)庫名__表名_索引名__
,可以使用下面的命令查看索引:
hive> SHOW TABLES '*idx*';
OK
default__user_behavior_idx_user_id_user_behavior__
Time taken: 0.044 seconds, Fetched: 1 row(s)
索引表包含索引列斟薇、HDFS的文件URI以及每行的偏移量师坎,可以通過下面命令查看:
-- 查看索引表結(jié)構(gòu)
hive> DESC default__user_behavior_idx_user_id_user_behavior__;
OK
user_id int
_bucketname string
_offsets array<bigint>
Time taken: 0.109 seconds, Fetched: 3 row(s)
-- 查看索引表內(nèi)容
hive> SELECT * FROM default__user_behavior_idx_user_id_user_behavior__;
OK
9 hdfs://cdh03:8020/user/hive/warehouse/user_behavior/userbehavior.csv [181]
7 hdfs://cdh03:8020/user/hive/warehouse/user_behavior/userbehavior.csv [136]
1 hdfs://cdh03:8020/user/hive/warehouse/user_behavior/userbehavior.csv [0]
6 hdfs://cdh03:8020/user/hive/warehouse/user_behavior/userbehavior.csv [113]
5 hdfs://cdh03:8020/user/hive/warehouse/user_behavior/userbehavior.csv [90]
10 hdfs://cdh03:8020/user/hive/warehouse/user_behavior/userbehavior.csv [205]
4 hdfs://cdh03:8020/user/hive/warehouse/user_behavior/userbehavior.csv [66]
8 hdfs://cdh03:8020/user/hive/warehouse/user_behavior/userbehavior.csv [158]
3 hdfs://cdh03:8020/user/hive/warehouse/user_behavior/userbehavior.csv [44]
2 hdfs://cdh03:8020/user/hive/warehouse/user_behavior/userbehavior.csv [22]
Time taken: 0.28 seconds, Fetched: 10 row(s)
如果要?jiǎng)h除索引,可以使用DROP INDEX命令堪滨,如下:
DROP INDEX idx_user_id_user_behavior ON user_behavior;
使用skewed/temporary表
Hive除了可以使用內(nèi)部表胯陋、外部表、分區(qū)表袱箱、分桶表之外遏乔,也可以使用skewed/temporary表,也可以在一定程度上提升性能发笔。
Hive從0.10版本之后開始支持skewed表盟萨,該表可以緩解數(shù)據(jù)傾斜。這種表之所以能夠提升性能筐咧,是因?yàn)榭梢宰詣?dòng)將造成數(shù)據(jù)傾斜的數(shù)據(jù)分割成不同的文件或者路徑鸯旁。使用示例如下:
CREATE TABLE sample_skewed_table (
dept_no int,
dept_name string
)
SKEWED BY (dept_no) ON (1000, 2000);-- 指定數(shù)據(jù)傾斜字段
另外,還可以使用temporary臨時(shí)表量蕊,將公共使用部分的數(shù)據(jù)集建成臨時(shí)表铺罢,同時(shí)臨時(shí)表支持SSD或memory的數(shù)據(jù)存儲(chǔ),從而可以提升性能残炮。
數(shù)據(jù)存儲(chǔ)優(yōu)化
文件格式
Hive支持TEXTFILE, SEQUENCEFILE, AVRO, RCFILE, ORC,以及PARQUET文件格式韭赘,可以通過兩種方式指定表的文件格式:
- CREATE TABLE ... STORE AS <file_format>:即在建表時(shí)指定文件格式,默認(rèn)是TEXTFILE
- ALTER TABLE ... [PARTITION partition_spec] SET FILEFORMAT <file_format>:修改具體表的文件格式
一旦存儲(chǔ)文件格式為TEXT的表被創(chuàng)建势就,可以直接通過load命令裝載一個(gè)text類型的文件泉瞻。我們可以先使用此命令將數(shù)據(jù)裝載到一張TEXT格式的表中,然后在通過INSERT OVERWRITE/INTO TABLE ... SELECT
命令將數(shù)據(jù)裝載到其他文件格式的表中苞冯。
尖叫提示:
如果要改變創(chuàng)建表的默認(rèn)文件格式袖牙,可以使用hive.default.fileformat=<file_format>進(jìn)行配置,改配置可以針對所有表舅锄。同時(shí)也可以使用hive.default.fileformat.managed =
<file_format>進(jìn)行配置鞭达,改配置僅適用于內(nèi)部表或外部表
TEXT, SEQUENCE和 AVRO文件是面向行的文件存儲(chǔ)格式,不是最佳的文件格式皇忿,因?yàn)榧幢闶侵徊樵円涣袛?shù)據(jù)畴蹭,使用這些存儲(chǔ)格式的表也需要讀取完整的一行數(shù)據(jù)。另一方面鳍烁,面向列的存儲(chǔ)格式(RCFILE, ORC, PARQUET)可以很好地解決上面的問題叨襟。關(guān)于每種文件格式的說明,如下:
- TEXTFILE
創(chuàng)建表時(shí)的默認(rèn)文件格式幔荒,數(shù)據(jù)被存儲(chǔ)成文本格式糊闽。文本文件可以被分割和并行處理梳玫,也可以使用壓縮,比如GZip墓怀、LZO或者Snappy汽纠。然而大部分的壓縮文件不支持分割和并行處理,會(huì)造成一個(gè)作業(yè)只有一個(gè)mapper去處理數(shù)據(jù)傀履,使用壓縮的文本文件要確保文件的不要過大虱朵,一般接近兩個(gè)HDFS塊的大小。
- SEQUENCEFILE
key/value對的二進(jìn)制存儲(chǔ)格式钓账,sequence文件的優(yōu)勢是比文本格式更好壓縮碴犬,sequence文件可以被壓縮成塊級別的記錄,塊級別的壓縮是一個(gè)很好的壓縮比例梆暮。如果使用塊壓縮服协,需要使用下面的配置:set hive.exec.compress.output=true; set io.seqfile.compression.type=BLOCK
- AVRO
二進(jìn)制格式文件,除此之外啦粹,avro也是一個(gè)序列化和反序列化的框架偿荷。avro提供了具體的數(shù)據(jù)schema。
- RCFILE
全稱是Record Columnar File唠椭,首先將表分為幾個(gè)行組跳纳,對每個(gè)行組內(nèi)的數(shù)據(jù)進(jìn)行按列存儲(chǔ),每一列的數(shù)據(jù)都是分開存儲(chǔ)贪嫂,即先水平劃分寺庄,再垂直劃分。
- ORC
全稱是Optimized Row Columnar力崇,從hive0.11版本開始支持斗塘,ORC格式是RCFILE格式的一種優(yōu)化的格式,提供了更大的默認(rèn)塊(256M)
- PARQUET
另外一種列式存儲(chǔ)的文件格式亮靴,與ORC非常類似馍盟,與ORC相比,Parquet格式支持的生態(tài)更廣茧吊,比如低版本的impala不支持orc格式
壓縮
壓縮技術(shù)可以減少map與reduce之間的數(shù)據(jù)傳輸贞岭,從而可以提升查詢性能,關(guān)于壓縮的配置可以在hive的命令行中或者h(yuǎn)ive-site.xml文件中進(jìn)行配置
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
存儲(chǔ)優(yōu)化
經(jīng)常被訪問的數(shù)據(jù)稱之為熱數(shù)據(jù)宪彩,可以針對熱數(shù)據(jù)提升查詢的性能休讳。比如通過增加熱數(shù)據(jù)的副本數(shù),可以增加數(shù)據(jù)本地性命中的可能性尿孔,從而提升查詢性能俊柔,當(dāng)然這要與存儲(chǔ)容量之間做出權(quán)衡筹麸。
$ hdfs dfs -setrep -R -w 4 /user/hive/warehouse/employee
注意,大量的小文件或者冗余副本會(huì)造成namenode節(jié)點(diǎn)內(nèi)存耗費(fèi)雏婶,尤其是大量小于HDFS塊大小的文件物赶。HDSF本身提供了應(yīng)對小文件的解決方案:
- Hadoop Archive/HAR:將小文件打包成大文件
- SEQUENCEFILE格式:將小文件壓縮成大文件
- CombineFileInputFormat:在map和reduce處理之前組合小文件
- HDFS Federation:HDFS聯(lián)盟,使用多個(gè)namenode節(jié)點(diǎn)管理文件
對于Hive而言留晚,可以使用下面的配置將查詢結(jié)果的文件進(jìn)行合并酵紫,從而避免產(chǎn)生小文件:
- hive.merge.mapfiles: 在一個(gè)僅有map的作業(yè)中,合并最后的結(jié)果文件错维,默認(rèn)為true
- hive.merge.mapredfiles:合并mapreduce作業(yè)的結(jié)果小文件 默認(rèn)false奖地,可以設(shè)置true
- hive.merge.size.per.task:定義合并文件的大小,默認(rèn) 256,000,000赋焕,即256MB
- hive.merge.smallfiles.avgsize: T觸發(fā)文件合并的文件大小閾值参歹,默認(rèn)值是16,000,000
當(dāng)一個(gè)作業(yè)的輸出結(jié)果文件的大小小于hive.merge.smallfiles.avgsize設(shè)定的閾值,并且hive.merge.mapfiles與hive.merge.mapredfiles設(shè)置為true隆判,Hive會(huì)額外啟動(dòng)一個(gè)mr作業(yè)將輸出小文件合并成大文件犬庇。
作業(yè)優(yōu)化
本地模式
當(dāng)Hive處理的數(shù)據(jù)量較小時(shí),啟動(dòng)分布式去處理數(shù)據(jù)會(huì)有點(diǎn)浪費(fèi)侨嘀,因?yàn)榭赡軉?dòng)的時(shí)間比數(shù)據(jù)處理的時(shí)間還要長臭挽,從Hive0.7版本之后,Hive支持將作業(yè)動(dòng)態(tài)地轉(zhuǎn)為本地模式飒炎,需要使用下面的配置:
SET hive.exec.mode.local.auto=true; -- 默認(rèn) false
SET hive.exec.mode.local.auto.inputbytes.max=50000000;
SET hive.exec.mode.local.auto.input.files.max=5; -- 默認(rèn) 4
一個(gè)作業(yè)只要滿足下面的條件埋哟,會(huì)啟用本地模式
- 輸入文件的大小小于
hive.exec.mode.local.auto.inputbytes.max
配置的大小 - map任務(wù)的數(shù)量小于
hive.exec.mode.local.auto.input.files.max
配置的大小 - reduce任務(wù)的數(shù)量是1或者0
JVM重用
默認(rèn)情況下,Hadoop會(huì)為為一個(gè)map或者reduce啟動(dòng)一個(gè)JVM郎汪,這樣可以并行執(zhí)行map和reduce赤赊。當(dāng)map或者reduce是那種僅運(yùn)行幾秒鐘的輕量級作業(yè)時(shí),JVM啟動(dòng)進(jìn)程所耗費(fèi)的時(shí)間會(huì)比作業(yè)執(zhí)行的時(shí)間還要長煞赢。Hadoop可以重用JVM抛计,通過共享JVM以串行而非并行的方式運(yùn)行map或者reduce。JVM的重用適用于同一個(gè)作業(yè)的map和reduce照筑,對于不同作業(yè)的task不能夠共享JVM吹截。如果要開啟JVM重用,需要配置一個(gè)作業(yè)最大task數(shù)量凝危,默認(rèn)值為1波俄,如果設(shè)置為-1,則表示不限制:
SET mapreduce.job.jvm.numtasks=5;
這個(gè)功能的缺點(diǎn)是蛾默,開啟JVM重用將一直占用使用到的task插槽懦铺,以便進(jìn)行重用,直到任務(wù)完成后才能釋放支鸡。如果某個(gè)“不平衡的”job中有某幾個(gè)reduce task執(zhí)行的時(shí)間要比其他Reduce task消耗的時(shí)間多的多的話冬念,那么保留的插槽就會(huì)一直空閑著卻無法被其他的job使用趁窃,直到所有的task都結(jié)束了才會(huì)釋放。
并行執(zhí)行
Hive的查詢通常會(huì)被轉(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í)行的效果不會(huì)很明顯。
Fetch模式
Fetch模式是指Hive中對某些情況的查詢可以不必使用MapReduce計(jì)算世吨÷肓冢可以簡單地讀取表對應(yīng)的存儲(chǔ)目錄下的文件,然后輸出查詢結(jié)果到控制臺另假。在開啟fetch模式之后像屋,在全局查找、字段查找边篮、limit查找等都啟動(dòng)mapreduce己莺,通過下面方式進(jìn)行配置:
hive.fetch.task.conversion=more
JOIN優(yōu)化
普通join
普通join又稱之為reduce端join,是一種最基本的join戈轿,并且耗時(shí)較長凌受。對于大表join小表,需要將大表放在右側(cè)思杯,即小表join大表胜蛉。新版的hive已經(jīng)對小表JOIN大表和大表JOIN小表進(jìn)行了優(yōu)化。小表放在左邊和右邊已經(jīng)沒有明顯區(qū)別色乾。
map端join
map端join適用于當(dāng)一張表很小(可以存在內(nèi)存中)的情況誊册,即可以將小表加載至內(nèi)存。Hive從0.7開始支持自動(dòng)轉(zhuǎn)為map端join暖璧,具體配置如下:
SET hive.auto.convert.join=true; -- hivev0.11.0之后默認(rèn)true
SET hive.mapjoin.smalltable.filesize=600000000; -- 默認(rèn) 25m
SET hive.auto.convert.join.noconditionaltask=true; -- 默認(rèn)true案怯,所以不需要指定map join hint
SET hive.auto.convert.join.noconditionaltask.size=10000000; -- 控制加載到內(nèi)存的表的大小
一旦開啟map端join配置,Hive會(huì)自動(dòng)檢查小表是否大于hive.mapjoin.smalltable.filesize
配置的大小澎办,如果大于則轉(zhuǎn)為普通的join嘲碱,如果小于則轉(zhuǎn)為map端join。
關(guān)于map端join的原理局蚀,如下圖所示:
首先麦锯,Task A(客戶端本地執(zhí)行的task)負(fù)責(zé)讀取小表a,并將其轉(zhuǎn)成一個(gè)HashTable的數(shù)據(jù)結(jié)構(gòu)琅绅,寫入到本地文件扶欣,之后將其加載至分布式緩存。
然后,Task B任務(wù)會(huì)啟動(dòng)map任務(wù)讀取大表b宵蛀,在Map階段,根據(jù)每條記錄與分布式緩存中的a表對應(yīng)的hashtable關(guān)聯(lián)县貌,并輸出結(jié)果
注意:map端join沒有reduce任務(wù)术陶,所以map直接輸出結(jié)果,即有多少個(gè)map任務(wù)就會(huì)產(chǎn)生多少個(gè)結(jié)果文件煤痕。
Bucket map join
bucket map join是一種特殊的map端join梧宫,主要區(qū)別是其應(yīng)用在分桶表上。如果要開啟分桶的map端join摆碉,需要開啟一下配置:
SET hive.auto.convert.join=true;
SET hive.optimize.bucketmapjoin=true; -- 默認(rèn)false
在一個(gè)分桶的map端join中塘匣,所有參與join的表必須是分桶表,并且join的字段是分桶字段(通過CLUSTERED BY指定)巷帝,另外忌卤,對于大表的分桶數(shù)量必須是小表分桶數(shù)量的倍數(shù)。
與普通的join相比楞泼,分桶join僅僅只讀取所需要的桶數(shù)據(jù)驰徊,不需要全表掃描。
Sort merge bucket (SMB) join
SMBjoin應(yīng)用與分桶表堕阔,如果兩張參與join的表是排序的棍厂,并且分桶字段相同,這樣可以使用sort-merge join超陆,其優(yōu)勢在于不用把小表完全加載至內(nèi)存中牺弹,會(huì)讀取兩張分桶表對應(yīng)的桶,執(zhí)行普通join(包括map與reduce)配置如下:
SET hive.input.format=
org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;
SET hive.auto.convert.sortmerge.join=true;
SET hive.optimize.bucketmapjoin=true;
SET hive.optimize.bucketmapjoin.sortedmerge=true;
SET hive.auto.convert.sortmerge.join.noconditionaltask=true;
Sort merge bucket map (SMBM) join
SMBM join是一種特殊的bucket map join时呀,與map端join不同的是张漂,不用將小表的所有數(shù)據(jù)行都加載至內(nèi)存中。使用SMBM join谨娜,參與join的表必須是排序的鹃锈,有著相同的分桶字段,并且join字段與分桶字段相同瞧预。配置如下:
SET hive.auto.convert.join=true;
SET hive.auto.convert.sortmerge.join=true
SET hive.optimize.bucketmapjoin=true;
SET hive.optimize.bucketmapjoin.sortedmerge=true;
SET hive.auto.convert.sortmerge.join.noconditionaltask=true;
SET hive.auto.convert.sortmerge.join.bigtable.selection.policy=
org.apache.hadoop.hive.ql.optimizer.TableSizeBasedBigTableSelectorForAutoSMJ;
Skew join
當(dāng)被處理的數(shù)據(jù)分布極其不均勻時(shí)屎债,會(huì)造成數(shù)據(jù)傾斜的現(xiàn)象。Hive可以通過如下的配置優(yōu)化數(shù)據(jù)傾斜的情況:
-- 默認(rèn)false垢油,如果數(shù)據(jù)傾斜盆驹,可以將其設(shè)置為true
SET hive.optimize.skewjoin=true;
-- 默認(rèn)為100000,如果key的數(shù)量大于配置的值滩愁,則超過的數(shù)量的key對應(yīng)的數(shù)據(jù)會(huì)被發(fā)送到其他的reduce任務(wù)
SET hive.skewjoin.key=100000;
尖叫提示:
數(shù)據(jù)傾斜在group by的情況下也會(huì)發(fā)生躯喇,所以可以開啟一個(gè)配置:set hive.groupby.skewindata=true,優(yōu)化group by出現(xiàn)的數(shù)據(jù)傾斜,一旦開啟之后廉丽,執(zhí)行作業(yè)時(shí)會(huì)首先額外觸發(fā)一個(gè)mr作業(yè)倦微,該作業(yè)的map任務(wù)的輸出會(huì)被隨機(jī)地分配到reduce任務(wù)上,從而避免數(shù)據(jù)傾斜
執(zhí)行引擎
Hive支持多種執(zhí)行引擎正压,比如spark欣福、tez。對于執(zhí)行引擎的選擇焦履,會(huì)影響整體的查詢性能拓劝。使用的配置如下:
SET hive.execution.engine=<engine>; -- <engine> = mr|tez|spark
- mr:默認(rèn)的執(zhí)行引擎,在Hive2.0版本版本中被標(biāo)記過時(shí)
- tez:可以將多個(gè)有依賴的作業(yè)轉(zhuǎn)換為一個(gè)作業(yè)嘉裤,這樣只需寫一次HDFS郑临,且中間節(jié)點(diǎn)較少,從而大大提升作業(yè)的計(jì)算性能屑宠。
- spark:一個(gè)通用的大數(shù)據(jù)計(jì)算框架厢洞,基于內(nèi)存計(jì)算,速度較快
優(yōu)化器
與關(guān)系型數(shù)據(jù)庫類似典奉,Hive會(huì)在真正執(zhí)行計(jì)算之前犀变,生成和優(yōu)化邏輯執(zhí)行計(jì)劃與物理執(zhí)行計(jì)劃。Hive有兩種優(yōu)化器:Vectorize(向量化優(yōu)化器)與Cost-Based Optimization (CBO,成本優(yōu)化器)秋柄。
向量化優(yōu)化器
向量化優(yōu)化器會(huì)同時(shí)處理大批量的數(shù)據(jù)获枝,而不是一行一行地處理。要使用這種向量化的操作骇笔,要求表的文件格式為ORC省店,配置如下:
SET hive.vectorized.execution.enabled=true; -- 默認(rèn) false
成本優(yōu)化器
Hive的CBO是基于apache Calcite的,Hive的CBO通過查詢成本(有analyze收集的統(tǒng)計(jì)信息)會(huì)生成有效率的執(zhí)行計(jì)劃笨触,最終會(huì)減少執(zhí)行的時(shí)間和資源的利用懦傍,使用CBO的配置如下:
SET hive.cbo.enable=true; --從 v0.14.0默認(rèn)true
SET hive.compute.query.using.stats=true; -- 默認(rèn)false
SET hive.stats.fetch.column.stats=true; -- 默認(rèn)false
SET hive.stats.fetch.partition.stats=true; -- 默認(rèn)true
總結(jié)
本文主要介紹了Hive調(diào)優(yōu)的基本思路÷樱總共分為四部分粗俱,首先介紹了調(diào)優(yōu)的基本工具使用(explain、analyze);接著從表設(shè)計(jì)層面介紹了一些優(yōu)化策略(分區(qū)虚吟、分桶寸认、索引);然后介紹了數(shù)據(jù)存儲(chǔ)方面的優(yōu)化(文件格式串慰、壓縮偏塞、存儲(chǔ)優(yōu)化);最后從作業(yè)層面介紹了優(yōu)化的技巧(開啟本地模式邦鲫、JVM重用灸叼、并行執(zhí)行、fetch模式、Join優(yōu)化古今、執(zhí)行引擎與優(yōu)化器)邻耕。本文主要為Hive性能調(diào)優(yōu)提供一些思路盒发,在實(shí)際的操作過程中需要具體問題具體分析贫橙∏砬唬總之一句話富弦,重劍無鋒峻黍,為作業(yè)分配合理的資源基本上可以滿足大部分的情況旬渠,適合的就是最好的甲献,沒有必要追求狂拽酷炫的技巧撬即,應(yīng)該把更多的精力放在業(yè)務(wù)問題上立磁,因?yàn)楣ぞ叩拇嬖诘膬r(jià)值是為了解決業(yè)務(wù)問題的,切不可本末倒置剥槐。
公眾號『大數(shù)據(jù)技術(shù)與數(shù)倉』唱歧,回復(fù)『資料』領(lǐng)取大數(shù)據(jù)資料包