數(shù)倉(cāng)|Hive性能調(diào)優(yōu)指北

在企業(yè)中使用Hive構(gòu)建離線數(shù)倉(cāng)是一種十分普遍的方案。盡管Hive的使用場(chǎng)景是通過(guò)批處理的方式處理大數(shù)據(jù),通常對(duì)處理時(shí)間不敏感。但是在資源有限的情況下,我們需要關(guān)注Hive的性能調(diào)優(yōu),從而方便數(shù)據(jù)的快速產(chǎn)出。同時(shí),關(guān)于Hive的性能調(diào)優(yōu),也是面試中比較常見(jiàn)的問(wèn)題,因此掌握Hive性能調(diào)優(yōu)的一些方法,不僅能夠在工作中提升效率而且還可以在面試中脫穎而出。本文會(huì)通過(guò)四個(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è)查看查詢(xún)性能的工具:explainanalyze,除此之外Hive的日志也提供了非常詳細(xì)的信息,方便查看執(zhí)行性能和報(bào)錯(cuò)排查。

善用explain語(yǔ)句

explain語(yǔ)句是查看執(zhí)行計(jì)劃經(jīng)常使用的一個(gè)工具,可以使用該語(yǔ)句分析查詢(xún)執(zhí)行計(jì)劃,具體使用語(yǔ)法如下:

EXPLAIN [FORMATTED|EXTENDED|DEPENDENCY|AUTHORIZATION] hql_query

上面的執(zhí)行語(yǔ)句中,有4個(gè)可選的關(guān)鍵字,其具體含義如下:

  • FORMATTED:對(duì)執(zhí)行計(jì)劃進(jìn)行格式化,返回JSON格式的執(zhí)行計(jì)劃
  • EXTENDED:提供一些額外的信息,比如文件的路徑信息
  • DEPENDENCY:以JSON格式返回查詢(xún)所依賴(lài)的表和分區(qū)的列表,從Hive0.10開(kāi)始使用,如下圖
  • AUTHORIZATION:列出需要被授權(quán)的條目,包括輸入與輸出,從Hive0.14開(kāi)始使用,如下圖

一個(gè)典型的查詢(xún)執(zhí)行計(jì)劃主要包括三部分,具體如下:

  • Abstract Syntax Tree (AST):抽象語(yǔ)法樹(shù),Hive使用一個(gè)稱(chēng)之為antlr的解析生成器,可以自動(dòng)地將HQL生成為抽象語(yǔ)法樹(shù)
  • Stage Dependencies:會(huì)列出運(yùn)行查詢(xún)所有的依賴(lài)以及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是抽象語(yǔ)法樹(shù)AST部分;STAGE
DEPENDENCIES
包括三個(gè)階段:Stage-0 、Stage-1及Stage-2,其中Stage-0 是root stage,即Stage-1與Stage-2依賴(lài)于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語(yǔ)句

analyze語(yǔ)句可以收集一些詳細(xì)的統(tǒng)計(jì)信息,比如表的行數(shù)、文件數(shù)、數(shù)據(jù)的大小等信息。這些統(tǒng)計(jì)信息作為元數(shù)據(jù)存儲(chǔ)在hive的元數(shù)據(jù)庫(kù)中。Hive支持表、分區(qū)和列級(jí)別的統(tǒng)計(jì)(與Impala類(lèi)似),這些信息作為Hive基于成本優(yōu)化策略(Cost-Based Optimizer (CBO))的輸入,該優(yōu)化器的主要作用是選擇耗費(fèi)最小系統(tǒng)資源的查詢(xún)計(jì)劃。其實(shí),在Hive3.2.0版本中,可以自動(dòng)收集這些統(tǒng)計(jì)信息,當(dāng)然也可以通過(guò)analyze語(yǔ)句進(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 ; 

尖叫提示

可以通過(guò)設(shè)置:SET hive.stats.autogather=true,進(jìn)行自動(dòng)收集統(tǒng)計(jì)信息,對(duì)于INSERT OVERWRITE/INTO操作的表或者分區(qū),可以自動(dòng)收集統(tǒng)計(jì)信息。值得注意的是,LOAD操作不能夠自動(dòng)收集統(tǒng)計(jì)信息

一旦這些統(tǒng)計(jì)信息收集完畢,可以通過(guò)DESCRIBE EXTENDED/FORMATTED語(yǔ)句查詢(xún)統(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ì)信息,通過(guò)查看日志信息,可以分析出導(dǎo)致作業(yè)執(zhí)行瓶頸的問(wèn)題,主要包括兩種類(lèi)型的日志:系統(tǒng)日志和作業(yè)日志。

系統(tǒng)日志包含了Hive運(yùn)行時(shí)的狀態(tài)等信息,可以通過(guò){HIVE_HOME}/conf/hive-log4j.properties文件進(jìn)行配置,主要的配置選項(xiàng)有:

hive.root.logger=WARN,DRFA ## 日志級(jí)別
hive.log.dir=/tmp/${user.name} ## 日志路徑
hive.log.file=hive.log ## 日志名稱(chēng)

也可以通過(guò)Hive cli命令行設(shè)置日志級(jí)別:$hive --hiveconf hive.root.logger=DEBUG,console這種方式只能在當(dāng)前會(huì)話生效。

作業(yè)日志所包含的作業(yè)信息通常是由YARN管理的,可以通過(guò)yarn logs -applicationId <application_id>命令查看作業(yè)日志。

設(shè)計(jì)優(yōu)化

分區(qū)表

對(duì)于一張比較大的表,將其設(shè)計(jì)成分區(qū)表可以提升查詢(xún)的性能,對(duì)于一個(gè)特定分區(qū)的查詢(xún),只會(huì)加載對(duì)應(yīng)分區(qū)路徑的文件數(shù)據(jù),所以執(zhí)行速度會(huì)比較快。值得注意的是,分區(qū)字段的選擇是影響查詢(xún)性能的重要因素,盡量避免層級(jí)較深的分區(qū),這樣會(huì)造成太多的子文件夾。一些常見(jiàn)的分區(qū)字段可以是:

  • 日期或者時(shí)間

比如year、month、day或者h(yuǎn)our,當(dāng)表中存在時(shí)間或者日期字段時(shí),可以使用些字段。

  • 地理位置

比如國(guó)家、省份、城市等

  • 業(yè)務(wù)邏輯

比如部門(mén)、銷(xiāo)售區(qū)域、客戶(hù)等等

分桶表

與分區(qū)表類(lèi)似,分桶表的組織方式是將HDFS上的文件分割成多個(gè)文件。分桶可以加快數(shù)據(jù)采樣,也可以提升join的性能(join的字段是分桶字段),因?yàn)榉滞翱梢源_保某個(gè)key對(duì)應(yīng)的數(shù)據(jù)在一個(gè)特定的桶內(nèi)(文件),所以巧妙地選擇分桶字段可以大幅度提升join的性能。通常情況下,分桶字段可以選擇經(jīng)常用在過(guò)濾操作或者join操作的字段。

索引

創(chuàng)建索引是關(guān)系型數(shù)據(jù)庫(kù)性能調(diào)優(yōu)的常見(jiàn)手段,在Hive中也不例外。Hive從0.7版本開(kāi)始支持索引,使用索引相比全表掃描而言,是一種比較廉價(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ǔ)的是索引列與其對(duì)應(yīng)的block id的pair對(duì)。除了此種索引外,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命令(見(jiàn)下面的示例),值得注意的是:當(dāng)基表(被創(chuàng)建索引的表)發(fā)生變化時(shí),該命令需要被再次執(zhí)行以便更新索引到最新的狀態(tài)。

ALTER INDEX idx_user_id_user_behavior ON user_behavior REBUILD;

一旦索引創(chuàng)建成功,會(huì)生成一張索引表,表的名稱(chēng)格式為:數(shù)據(jù)庫(kù)名__表名_索引名__,可以使用下面的命令查看索引:

hive> SHOW TABLES '*idx*';
OK
default__user_behavior_idx_user_id_user_behavior__
Time taken: 0.044 seconds, Fetched: 1 row(s)

索引表包含索引列、HDFS的文件URI以及每行的偏移量,可以通過(guò)下面命令查看:

-- 查看索引表結(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版本之后開(kāi)始支持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文件格式,可以通過(guò)兩種方式指定表的文件格式:

  • CREATE TABLE ... STORE AS <file_format>:即在建表時(shí)指定文件格式,默認(rèn)是TEXTFILE
  • ALTER TABLE ... [PARTITION partition_spec] SET FILEFORMAT <file_format>:修改具體表的文件格式

一旦存儲(chǔ)文件格式為T(mén)EXT的表被創(chuàng)建,可以直接通過(guò)load命令裝載一個(gè)text類(lèi)型的文件。我們可以先使用此命令將數(shù)據(jù)裝載到一張TEXT格式的表中,然后在通過(guò)INSERT OVERWRITE/INTO TABLE ... SELECT命令將數(shù)據(jù)裝載到其他文件格式的表中。

尖叫提示

如果要改變創(chuàng)建表的默認(rèn)文件格式,可以使用hive.default.fileformat=<file_format>進(jìn)行配置,改配置可以針對(duì)所有表。同時(shí)也可以使用hive.default.fileformat.managed =
<file_format>進(jìn)行配置,改配置僅適用于內(nèi)部表或外部表

TEXT, SEQUENCE和 AVRO文件是面向行的文件存儲(chǔ)格式,不是最佳的文件格式,因?yàn)榧幢闶侵徊樵?xún)一列數(shù)據(jù),使用這些存儲(chǔ)格式的表也需要讀取完整的一行數(shù)據(jù)。另一方面,面向列的存儲(chǔ)格式(RCFILE, ORC, PARQUET)可以很好地解決上面的問(wèn)題。關(guān)于每種文件格式的說(shuō)明,如下:

  • TEXTFILE

創(chuàng)建表時(shí)的默認(rèn)文件格式,數(shù)據(jù)被存儲(chǔ)成文本格式。文本文件可以被分割和并行處理,也可以使用壓縮,比如GZip、LZO或者Snappy。然而大部分的壓縮文件不支持分割和并行處理,會(huì)造成一個(gè)作業(yè)只有一個(gè)mapper去處理數(shù)據(jù),使用壓縮的文本文件要確保文件的不要過(guò)大,一般接近兩個(gè)HDFS塊的大小。

  • SEQUENCEFILE

key/value對(duì)的二進(jìn)制存儲(chǔ)格式,sequence文件的優(yōu)勢(shì)是比文本格式更好壓縮,sequence文件可以被壓縮成塊級(jí)別的記錄,塊級(jí)別的壓縮是一個(gè)很好的壓縮比例。如果使用塊壓縮,需要使用下面的配置:set hive.exec.compress.output=true; set io.seqfile.compression.type=BLOCK

  • AVRO

二進(jìn)制格式文件,除此之外,avro也是一個(gè)序列化和反序列化的框架。avro提供了具體的數(shù)據(jù)schema。

  • RCFILE

全稱(chēng)是Record Columnar File,首先將表分為幾個(gè)行組,對(duì)每個(gè)行組內(nèi)的數(shù)據(jù)進(jìn)行按列存儲(chǔ),每一列的數(shù)據(jù)都是分開(kāi)存儲(chǔ),即先水平劃分,再垂直劃分。

  • ORC

全稱(chēng)是Optimized Row Columnar,從hive0.11版本開(kāi)始支持,ORC格式是RCFILE格式的一種優(yōu)化的格式,提供了更大的默認(rèn)塊(256M)

  • PARQUET

另外一種列式存儲(chǔ)的文件格式,與ORC非常類(lèi)似,與ORC相比,Parquet格式支持的生態(tài)更廣,比如低版本的impala不支持orc格式

壓縮

壓縮技術(shù)可以減少map與reduce之間的數(shù)據(jù)傳輸,從而可以提升查詢(xún)性能,關(guān)于壓縮的配置可以在hive的命令行中或者h(yuǎn)ive-site.xml文件中進(jìn)行配置

SET hive.exec.compress.intermediate=true

開(kāi)啟壓縮之后,可以選擇下面的壓縮格式:


關(guān)于壓縮的編碼器可以通過(guò)mapred-site.xml, hive-site.xml進(jìn)行配置,也可以通過(guò)命令行進(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)常被訪問(wèn)的數(shù)據(jù)稱(chēng)之為熱數(shù)據(jù),可以針對(duì)熱數(shù)據(jù)提升查詢(xún)的性能。比如通過(guò)增加熱數(shù)據(jù)的副本數(shù),可以增加數(shù)據(jù)本地性命中的可能性,從而提升查詢(xún)性能,當(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)對(duì)小文件的解決方案:

  • Hadoop Archive/HAR:將小文件打包成大文件
  • SEQUENCEFILE格式:將小文件壓縮成大文件
  • CombineFileInputFormat:在map和reduce處理之前組合小文件
  • HDFS Federation:HDFS聯(lián)盟,使用多個(gè)namenode節(jié)點(diǎn)管理文件

對(duì)于Hive而言,可以使用下面的配置將查詢(xún)結(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í)間還要長(zhǎng),從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)行幾秒鐘的輕量級(jí)作業(yè)時(shí),JVM啟動(dòng)進(jìn)程所耗費(fèi)的時(shí)間會(huì)比作業(yè)執(zhí)行的時(shí)間還要長(zhǎng)。Hadoop可以重用JVM,通過(guò)共享JVM以串行而非并行的方式運(yùn)行map或者reduce。JVM的重用適用于同一個(gè)作業(yè)的map和reduce,對(duì)于不同作業(yè)的task不能夠共享JVM。如果要開(kāi)啟JVM重用,需要配置一個(gè)作業(yè)最大task數(shù)量,默認(rèn)值為1,如果設(shè)置為-1,則表示不限制:

SET mapreduce.job.jvm.numtasks=5;

這個(gè)功能的缺點(diǎn)是,開(kāi)啟JVM重用將一直占用使用到的task插槽,以便進(jìn)行重用,直到任務(wù)完成后才能釋放。如果某個(gè)“不平衡的”job中有某幾個(gè)reduce task執(zhí)行的時(shí)間要比其他Reduce task消耗的時(shí)間多的多的話,那么保留的插槽就會(huì)一直空閑著卻無(wú)法被其他的job使用,直到所有的task都結(jié)束了才會(huì)釋放。

并行執(zhí)行

Hive的查詢(xún)通常會(huì)被轉(zhuǎn)換成一系列的stage,這些stage之間并不是一直相互依賴(lài)的,所以可以并行執(zhí)行這些stage,可以通過(guò)下面的方式進(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中對(duì)某些情況的查詢(xún)可以不必使用MapReduce計(jì)算??梢院?jiǎn)單地讀取表對(duì)應(yīng)的存儲(chǔ)目錄下的文件,然后輸出查詢(xún)結(jié)果到控制臺(tái)。在開(kāi)啟fetch模式之后,在全局查找、字段查找、limit查找等都啟動(dòng)mapreduce,通過(guò)下面方式進(jìn)行配置:

hive.fetch.task.conversion=more

JOIN優(yōu)化

普通join

普通join又稱(chēng)之為reduce端join,是一種最基本的join,并且耗時(shí)較長(zhǎng)。對(duì)于大表join小表,需要將大表放在右側(cè),即小表join大表。新版的hive已經(jīng)對(duì)小表JOIN大表和大表JOIN小表進(jìn)行了優(yōu)化。小表放在左邊和右邊已經(jīng)沒(méi)有明顯區(qū)別。

map端join

map端join適用于當(dāng)一張表很小(可以存在內(nèi)存中)的情況,即可以將小表加載至內(nèi)存。Hive從0.7開(kāi)始支持自動(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)存的表的大小

一旦開(kāi)啟map端join配置,Hive會(huì)自動(dòng)檢查小表是否大于hive.mapjoin.smalltable.filesize配置的大小,如果大于則轉(zhuǎn)為普通的join,如果小于則轉(zhuǎn)為map端join。

關(guān)于map端join的原理,如下圖所示:

首先,Task A(客戶(hù)端本地執(zhí)行的task)負(fù)責(zé)讀取小表a,并將其轉(zhuǎn)成一個(gè)HashTable的數(shù)據(jù)結(jié)構(gòu),寫(xiě)入到本地文件,之后將其加載至分布式緩存。

然后,Task B任務(wù)會(huì)啟動(dòng)map任務(wù)讀取大表b,在Map階段,根據(jù)每條記錄與分布式緩存中的a表對(duì)應(yīng)的hashtable關(guān)聯(lián),并輸出結(jié)果

注意:map端join沒(méi)有reduce任務(wù),所以map直接輸出結(jié)果,即有多少個(gè)map任務(wù)就會(huì)產(chǎn)生多少個(gè)結(jié)果文件。

Bucket map join

bucket map join是一種特殊的map端join,主要區(qū)別是其應(yīng)用在分桶表上。如果要開(kāi)啟分桶的map端join,需要開(kāi)啟一下配置:

SET hive.auto.convert.join=true;
SET hive.optimize.bucketmapjoin=true; -- 默認(rèn)false

在一個(gè)分桶的map端join中,所有參與join的表必須是分桶表,并且join的字段是分桶字段(通過(guò)CLUSTERED BY指定),另外,對(duì)于大表的分桶數(shù)量必須是小表分桶數(shù)量的倍數(shù)。

與普通的join相比,分桶join僅僅只讀取所需要的桶數(shù)據(jù),不需要全表掃描。

Sort merge bucket (SMB) join

SMBjoin應(yīng)用與分桶表,如果兩張參與join的表是排序的,并且分桶字段相同,這樣可以使用sort-merge join,其優(yōu)勢(shì)在于不用把小表完全加載至內(nèi)存中,會(huì)讀取兩張分桶表對(duì)應(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可以通過(guò)如下的配置優(yōu)化數(shù)據(jù)傾斜的情況:

-- 默認(rèn)false,如果數(shù)據(jù)傾斜,可以將其設(shè)置為true
SET hive.optimize.skewjoin=true;
-- 默認(rèn)為100000,如果key的數(shù)量大于配置的值,則超過(guò)的數(shù)量的key對(duì)應(yīng)的數(shù)據(jù)會(huì)被發(fā)送到其他的reduce任務(wù)
SET hive.skewjoin.key=100000;

尖叫提示

數(shù)據(jù)傾斜在group by的情況下也會(huì)發(fā)生,所以可以開(kāi)啟一個(gè)配置:set hive.groupby.skewindata=true,優(yōu)化group by出現(xiàn)的數(shù)據(jù)傾斜,一旦開(kāi)啟之后,執(zhí)行作業(yè)時(shí)會(huì)首先額外觸發(fā)一個(gè)mr作業(yè),該作業(yè)的map任務(wù)的輸出會(huì)被隨機(jī)地分配到reduce任務(wù)上,從而避免數(shù)據(jù)傾斜

執(zhí)行引擎

Hive支持多種執(zhí)行引擎,比如spark、tez。對(duì)于執(zhí)行引擎的選擇,會(huì)影響整體的查詢(xún)性能。使用的配置如下:

SET hive.execution.engine=<engine>; -- <engine> = mr|tez|spark
  • mr:默認(rèn)的執(zhí)行引擎,在Hive2.0版本版本中被標(biāo)記過(guò)時(shí)
  • tez:可以將多個(gè)有依賴(lài)的作業(yè)轉(zhuǎn)換為一個(gè)作業(yè),這樣只需寫(xiě)一次HDFS,且中間節(jié)點(diǎn)較少,從而大大提升作業(yè)的計(jì)算性能。
  • spark:一個(gè)通用的大數(shù)據(jù)計(jì)算框架,基于內(nèi)存計(jì)算,速度較快

優(yōu)化器

與關(guān)系型數(shù)據(jù)庫(kù)類(lèi)似,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通過(guò)查詢(xún)成本(有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)化的技巧(開(kāi)啟本地模式、JVM重用、并行執(zhí)行、fetch模式、Join優(yōu)化、執(zhí)行引擎與優(yōu)化器)。本文主要為Hive性能調(diào)優(yōu)提供一些思路,在實(shí)際的操作過(guò)程中需要具體問(wèn)題具體分析??傊痪湓挘貏o(wú)鋒,為作業(yè)分配合理的資源基本上可以滿足大部分的情況,適合的就是最好的,沒(méi)有必要追求狂拽酷炫的技巧,應(yīng)該把更多的精力放在業(yè)務(wù)問(wèn)題上,因?yàn)楣ぞ叩拇嬖诘膬r(jià)值是為了解決業(yè)務(wù)問(wèn)題的,切不可本末倒置。

公眾號(hào)『大數(shù)據(jù)技術(shù)與數(shù)倉(cāng)』,回復(fù)『資料』領(lǐng)取大數(shù)據(jù)資料包

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容