关于hive:数仓Hive性能调优指北

4次阅读

共计 15226 个字符,预计需要花费 39 分钟才能阅读完成。

在企业中应用 Hive 构建离线数仓是一种非常广泛的计划。只管 Hive 的应用场景是通过批处理的形式解决大数据,通常对解决工夫不敏感。然而在资源无限的状况下,咱们须要关注 Hive 的性能调优,从而不便数据的疾速产出。同时,对于 Hive 的性能调优,也是面试中比拟常见的问题,因而把握 Hive 性能调优的一些办法,不仅可能在工作中晋升效率而且还能够在面试中怀才不遇。本文会通过四个方面介绍 Hive 性能调优,次要包含:

  • 性能调优的工具
  • 设计优化
  • 数据存储优化
  • 作业优化

性能调优的工具

HQL 提供了两个查看查问性能的工具:explainanalyze,除此之外 Hive 的日志也提供了十分具体的信息,不便查看执行性能和报错排查。

善用 explain 语句

explain 语句是查看执行打算常常应用的一个工具,能够应用该语句剖析查问执行打算,具体应用语法如下:

EXPLAIN [FORMATTED|EXTENDED|DEPENDENCY|AUTHORIZATION] hql_query

下面的执行语句中,有 4 个可选的关键字,其具体含意如下:

  • FORMATTED:对执行打算进行格式化,返回 JSON 格局的执行打算
  • EXTENDED:提供一些额定的信息,比方文件的门路信息
  • DEPENDENCY:以 JSON 格局返回查问所依赖的表和分区的列表,从 Hive0.10 开始应用,如下图

  • AUTHORIZATION:列出须要被受权的条目,包含输出与输入,从 Hive0.14 开始应用, 如下图

一个典型的查问执行打算次要包含三局部,具体如下:

  • Abstract Syntax Tree (AST):形象语法树,Hive 应用一个称之为 antlr 的解析生成器,能够主动地将 HQL 生成为形象语法树
  • Stage Dependencies:会列出运行查问所有的依赖以及 stage 的数量
  • Stage Plans:蕴含了十分重要的信息,比方运行作业时的 operator 和 sort orders

举个栗子

假如有一张表:

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 ':';

查看执行打算:

EXPLAIN
SELECT gender_age.gender,
       count(*)
FROM employee_partitioned
WHERE YEAR=2020
GROUP BY gender_age.gender
LIMIT 2;

执行打算概览:

如上图:Map/Reduce operator tree是形象语法树 AST 局部;**STAGE
DEPENDENCIES包含三个阶段:Stage-0、Stage- 1 及 Stage-2,其中 Stage-0 是 root stage,即 Stage- 1 与 Stage- 2 依赖于 Stage-0;STAGE PLANS** 局部,Stage- 1 与 Stage2 都蕴含一个 Map Operator Tree 和一个 Reduce Operator Tree,Stage- 0 不蕴含 map 和 reduce,仅仅是一个 fetch 数据的操作。

执行打算详细信息:

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 语句能够收集一些具体的统计信息,比方表的行数、文件数、数据的大小等信息。这些统计信息作为元数据存储在 hive 的元数据库中。Hive 反对表、分区和列级别的统计 (与 Impala 相似),这些信息作为 Hive 基于老本优化策略(Cost-Based Optimizer (CBO)) 的输出, 该优化器的次要作用是抉择消耗最小系统资源的查问打算。其实,在 Hive3.2.0 版本中,能够主动收集这些统计信息,当然也能够通过 analyze 语句进行手动统计表、分区或者字段的信息。具体的应用形式如下:

  • 1. 收集表的统计信息(非分区表),当指定 NOSCAN 关键字时,会疏忽扫描文件内容,仅仅统计文件的数量与大小,速度会比拟快
-- 不应用 NOSCAN 关键字
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 关键字
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. 收集分区表的统计信息
-- 收集具体分区的统计信息
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

-- 收集所有分区的统计信息
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. 收集表的某个字段的统计信息
hive> ANALYZE TABLE user_behavior COMPUTE STATISTICS FOR COLUMNS user_id ; 

尖叫提醒

能够通过设置:SET hive.stats.autogather=true,进行主动收集统计信息,对于 INSERT OVERWRITE/INTO 操作的表或者分区,能够主动收集统计信息。值得注意的是,LOAD 操作不可能主动收集统计信息

一旦这些统计信息收集结束,能够通过 DESCRIBE EXTENDED/FORMATTED 语句查问统计信息,具体应用如下:

-- 查看一个分区的统计信息
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 
...
-- 查看一张表的统计信息
hive> DESCRIBE FORMATTED employee_partitioned;
...
Table Parameters:                
        numPartitions           1                   
        transient_lastDdlTime   1591431482 
...
-- 查看某列的统计信息
hive> DESCRIBE FORMATTED  user_behavior.user_id;

罕用日志剖析

日志提供了 job 运行的详细信息,通过查看日志信息,能够剖析出导致作业执行瓶颈的问题,次要包含两种类型的日志:系统日志和作业日志。

系统日志蕴含了 Hive 运行时的状态等信息,能够通过{HIVE_HOME}/conf/hive-log4j.properties 文件进行配置,次要的配置选项有:

hive.root.logger=WARN,DRFA ## 日志级别
hive.log.dir=/tmp/${user.name} ## 日志门路
hive.log.file=hive.log ## 日志名称

也能够通过 Hive cli 命令行设置日志级别:$hive --hiveconf hive.root.logger=DEBUG,console这种形式只能在以后会话失效。

作业日志所蕴含的作业信息通常是由 YARN 治理的,能够通过 yarn logs -applicationId <application_id> 命令查看作业日志。

设计优化

分区表

对于一张比拟大的表,将其设计成分区表能够晋升查问的性能,对于一个特定分区的查问,只会加载对应分区门路的文件数据,所以执行速度会比拟快。值得注意的是,分区字段的抉择是影响查问性能的重要因素,尽量避免层级较深的分区,这样会造成太多的子文件夹。一些常见的分区字段能够是:

  • 日期或者工夫

比方 year、month、day 或者 hour,当表中存在工夫或者日期字段时,能够应用些字段。

  • 地理位置

比方国家、省份、城市等

  • 业务逻辑

比方部门、销售区域、客户等等

分桶表

与分区表相似,分桶表的组织形式是将 HDFS 上的文件宰割成多个文件。分桶能够放慢数据采样,也能够晋升 join 的性能(join 的字段是分桶字段),因为分桶能够确保某个 key 对应的数据在一个特定的桶内(文件),所以奇妙地抉择分桶字段能够大幅度晋升 join 的性能。通常状况下,分桶字段能够抉择常常用在过滤操作或者 join 操作的字段。

索引

创立索引是关系型数据库性能调优的常见伎俩,在 Hive 中也不例外。Hive 从 0.7 版本开始反对索引,应用索引相比全表扫描而言,是一种比拟便宜的操作,Hive 中创立索引的形式如下:

CREATE INDEX idx_user_id_user_behavior
ON TABLE user_behavior (user_id)
AS 'COMPACT'
WITH DEFERRED REBUILD;

下面创立的是 COMPACT 索引,存储的是索引列与其对应的 block id 的 pair 对。除了此种索引外,Hive 还反对位图索引(BITMAP), 应用形式如下:

CREATE INDEX idx_behavior_user_behavior
ON TABLE user_behavior (behavior)
AS 'BITMAP'
WITH DEFERRED REBUILD;

下面创立的索引时,应用了 WITH DEFERRED REBUILD 选项,该选项能够防止索引立刻被创立,当建设索引时,能够应用 LTER...REBUILD 命令 (见上面的示例),值得注意的是:当基表(被创立索引的表) 发生变化时,该命令须要被再次执行以便更新索引到最新的状态。

ALTER INDEX idx_user_id_user_behavior ON user_behavior REBUILD;

一旦索引创立胜利,会生成一张索引表,表的名称格局为:数据库名__表名_索引名__,能够应用上面的命令查看索引:

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

索引表蕴含索引列、HDFS 的文件 URI 以及每行的偏移量,能够通过上面命令查看:

-- 查看索引表构造
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)
-- 查看索引表内容
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)

如果要删除索引,能够应用 DROP INDEX 命令,如下:

DROP INDEX idx_user_id_user_behavior ON user_behavior;

应用 skewed/temporary 表

Hive 除了能够应用外部表、内部表、分区表、分桶表之外,也能够应用 skewed/temporary 表,也能够在肯定水平上晋升性能。

Hive 从 0.10 版本之后开始反对 skewed 表,该表能够缓解数据歪斜。这种表之所以可能晋升性能,是因为能够主动将造成数据歪斜的数据宰割成不同的文件或者门路。应用示例如下:

CREATE TABLE sample_skewed_table (
dept_no int, 
dept_name string
) 
SKEWED BY (dept_no) ON (1000, 2000);-- 指定数据歪斜字段

另外,还能够应用 temporary 长期表,将公共应用局部的数据集建成长期表,同时长期表反对 SSD 或 memory 的数据存储,从而能够晋升性能。

数据存储优化

文件格式

Hive 反对 TEXTFILE, SEQUENCEFILE, AVRO, RCFILE, ORC, 以及 PARQUET 文件格式,能够通过两种形式指定表的文件格式:

  • CREATE TABLE … STORE AS <file_format>: 即在建表时指定文件格式,默认是 TEXTFILE
  • ALTER TABLE … [PARTITION partition_spec] SET FILEFORMAT <file_format>: 批改具体表的文件格式

一旦存储文件格式为 TEXT 的表被创立,能够间接通过 load 命令装载一个 text 类型的文件。咱们能够先应用此命令将数据装载到一张 TEXT 格局的表中,而后在通过 INSERT OVERWRITE/INTO TABLE ... SELECT 命令将数据装载到其余文件格式的表中。

尖叫提醒

如果要扭转创立表的默认文件格式,能够应用 hive.default.fileformat=<file_format> 进行配置,改配置能够针对所有表。同时也能够应用 hive.default.fileformat.managed =
<file_format> 进行配置,改配置仅实用于外部表或内部表

TEXT, SEQUENCE 和 AVRO 文件是面向行的文件存储格局,不是最佳的文件格式,因为即使是只查问一列数据,应用这些存储格局的表也须要读取残缺的一行数据。另一方面,面向列的存储格局 (RCFILE, ORC, PARQUET) 能够很好地解决下面的问题。对于每种文件格式的阐明,如下:

  • TEXTFILE

创立表时的默认文件格式,数据被存储成文本格式。文本文件能够被宰割和并行处理,也能够应用压缩,比方 GZip、LZO 或者 Snappy。然而大部分的压缩文件不反对宰割和并行处理,会造成一个作业只有一个 mapper 去解决数据,应用压缩的文本文件要确保文件的不要过大,个别靠近两个 HDFS 块的大小。

  • SEQUENCEFILE

key/value 对的二进制存储格局,sequence 文件的劣势是比文本格式更好压缩,sequence 文件能够被压缩成块级别的记录,块级别的压缩是一个很好的压缩比例。如果应用块压缩,须要应用上面的配置:set hive.exec.compress.output=true; set io.seqfile.compression.type=BLOCK

  • AVRO

二进制格式文件,除此之外,avro 也是一个序列化和反序列化的框架。avro 提供了具体的数据 schema。

  • RCFILE

全称是 Record Columnar File,首先将表分为几个行组,对每个行组内的数据进行按列存储,每一列的数据都是离开存储,即先程度划分,再垂直划分。

  • ORC

全称是 Optimized Row Columnar,从 hive0.11 版本开始反对,ORC 格局是 RCFILE 格局的一种优化的格局,提供了更大的默认块(256M)

  • PARQUET

另外一种列式存储的文件格式,与 ORC 十分相似,与 ORC 相比,Parquet 格局反对的生态更广,比方低版本的 impala 不反对 orc 格局

压缩

压缩技术能够缩小 map 与 reduce 之间的数据传输,从而能够晋升查问性能,对于压缩的配置能够在 hive 的命令行中或者 hive-site.xml 文件中进行配置

SET hive.exec.compress.intermediate=true

开启压缩之后,能够抉择上面的压缩格局:

对于压缩的编码器能够通过 mapred-site.xml, hive-site.xml 进行配置,也能够通过命令行进行配置, 比方:

-- 两头后果压缩
SET hive.intermediate.compression.codec=org.apache.hadoop.io.compress.SnappyCodec
-- 输入后果压缩
SET hive.exec.compress.output=true;
SET mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.SnappyCodc

存储优化

常常被拜访的数据称之为热数据,能够针对热数据晋升查问的性能。比方通过减少热数据的正本数,能够减少数据本地性命中的可能性,从而晋升查问性能,当然这要与存储容量之间做出衡量。

$ hdfs dfs -setrep -R -w 4 /user/hive/warehouse/employee

留神,大量的小文件或者冗余正本会造成 namenode 节点内存消耗,尤其是大量小于 HDFS 块大小的文件。HDSF 自身提供了应答小文件的解决方案:

  • Hadoop Archive/HAR: 将小文件打包成大文件
  • SEQUENCEFILE 格局:将小文件压缩成大文件
  • CombineFileInputFormat: 在 map 和 reduce 解决之前组合小文件
  • HDFS Federation:HDFS 联盟,应用多个 namenode 节点管理文件

对于 Hive 而言,能够应用上面的配置将查问后果的文件进行合并,从而防止产生小文件:

  • hive.merge.mapfiles: 在一个仅有 map 的作业中,合并最初的后果文件,默认为 true
  • hive.merge.mapredfiles: 合并 mapreduce 作业的后果小文件 默认 false,能够设置 true
  • hive.merge.size.per.task: 定义合并文件的大小,默认 256,000,000,即 256MB
  • hive.merge.smallfiles.avgsize: T 触发文件合并的文件大小阈值,默认值是 16,000,000

当一个作业的输入后果文件的大小小于 hive.merge.smallfiles.avgsize 设定的阈值,并且 hive.merge.mapfiles 与 hive.merge.mapredfiles 设置为 true,Hive 会额定启动一个 mr 作业将输入小文件合并成大文件。

作业优化

本地模式

当 Hive 解决的数据量较小时,启动分布式去解决数据会有点节约,因为可能启动的工夫比数据处理的工夫还要长,从 Hive0.7 版本之后,Hive 反对将作业动静地转为本地模式,须要应用上面的配置:

SET hive.exec.mode.local.auto=true; -- 默认 false
SET hive.exec.mode.local.auto.inputbytes.max=50000000;
SET hive.exec.mode.local.auto.input.files.max=5; -- 默认 4

一个作业只有满足上面的条件,会启用本地模式

  • 输出文件的大小小于 hive.exec.mode.local.auto.inputbytes.max 配置的大小
  • map 工作的数量小于 hive.exec.mode.local.auto.input.files.max 配置的大小
  • reduce 工作的数量是 1 或者 0

JVM 重用

默认状况下,Hadoop 会为为一个 map 或者 reduce 启动一个 JVM,这样能够并行执行 map 和 reduce。当 map 或者 reduce 是那种仅运行几秒钟的轻量级作业时,JVM 启动过程所消耗的工夫会比作业执行的工夫还要长。Hadoop 能够重用 JVM,通过共享 JVM 以串行而非并行的形式运行 map 或者 reduce。JVM 的重用实用于同一个作业的 map 和 reduce,对于不同作业的 task 不可能共享 JVM。如果要开启 JVM 重用,须要配置一个作业最大 task 数量,默认值为 1,如果设置为 -1,则示意不限度:

SET mapreduce.job.jvm.numtasks=5;

这个性能的毛病是,开启 JVM 重用将始终占用应用到的 task 插槽,以便进行重用,直到工作实现后能力开释。如果某个“不均衡的”job 中有某几个 reduce task 执行的工夫要比其余 Reduce task 耗费的工夫多的多的话,那么保留的插槽就会始终闲暇着却无奈被其余的 job 应用,直到所有的 task 都完结了才会开释。

并行执行

Hive 的查问通常会被转换成一系列的 stage,这些 stage 之间并不是始终相互依赖的,所以能够并行执行这些 stage,能够通过上面的形式进行配置:

SET hive.exec.parallel=true; -- 默认 false
SET hive.exec.parallel.thread.number=16; -- 默认 8 

并行执行能够减少集群资源的利用率,如果集群的资源使用率曾经很高了,那么并行执行的成果不会很显著。

Fetch 模式

Fetch 模式是指 Hive 中对某些状况的查问能够不用应用 MapReduce 计算。能够简略地读取表对应的存储目录下的文件,而后输入查问后果到控制台。在开启 fetch 模式之后,在全局查找、字段查找、limit 查找等都启动 mapreduce,通过上面形式进行配置:

hive.fetch.task.conversion=more

JOIN 优化

一般 join

一般 join 又称之为 reduce 端 join,是一种最根本的 join,并且耗时较长。对于大表 join 小表,须要将大表放在右侧,即小表 join 大表。新版的 hive 曾经对小表 JOIN 大表和大表 JOIN 小表进行了优化。小表放在右边和左边曾经没有显著区别。

map 端 join

map 端 join 实用于当一张表很小 (能够存在内存中) 的状况,即能够将小表加载至内存。Hive 从 0.7 开始反对主动转为 map 端 join,具体配置如下:

SET hive.auto.convert.join=true; --  hivev0.11.0 之后默认 true
SET hive.mapjoin.smalltable.filesize=600000000; -- 默认 25m
SET hive.auto.convert.join.noconditionaltask=true; -- 默认 true,所以不须要指定 map join hint
SET hive.auto.convert.join.noconditionaltask.size=10000000; -- 管制加载到内存的表的大小

一旦开启 map 端 join 配置,Hive 会主动查看小表是否大于 hive.mapjoin.smalltable.filesize 配置的大小,如果大于则转为一般的 join,如果小于则转为 map 端 join。

对于 map 端 join 的原理,如下图所示:

首先,Task A(客户端本地执行的 task)负责读取小表 a,并将其转成一个 HashTable 的数据结构,写入到本地文件,之后将其加载至分布式缓存。

而后,Task B 工作会启动 map 工作读取大表 b,在 Map 阶段,依据每条记录与分布式缓存中的 a 表对应的 hashtable 关联,并输入后果

留神:map 端 join 没有 reduce 工作,所以 map 间接输入后果,即有多少个 map 工作就会产生多少个后果文件。

Bucket map join

bucket map join 是一种非凡的 map 端 join,次要区别是其利用在分桶表上。如果要开启分桶的 map 端 join,须要开启一下配置:

SET hive.auto.convert.join=true;
SET hive.optimize.bucketmapjoin=true; -- 默认 false

在一个分桶的 map 端 join 中,所有参加 join 的表必须是分桶表,并且 join 的字段是分桶字段(通过 CLUSTERED BY 指定),另外,对于大表的分桶数量必须是小表分桶数量的倍数。

与一般的 join 相比,分桶 join 仅仅只读取所须要的桶数据,不须要全表扫描。

Sort merge bucket (SMB) join

SMBjoin 利用与分桶表,如果两张参加 join 的表是排序的,并且分桶字段雷同,这样能够应用 sort-merge join,其劣势在于不必把小表齐全加载至内存中,会读取两张分桶表对应的桶,执行一般 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 不同的是,不必将小表的所有数据行都加载至内存中。应用 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

当被解决的数据分布极其不平均时,会造成数据歪斜的景象。Hive 能够通过如下的配置优化数据歪斜的状况:

-- 默认 false,如果数据歪斜,能够将其设置为 true
SET hive.optimize.skewjoin=true;
-- 默认为 100000,如果 key 的数量大于配置的值,则超过的数量的 key 对应的数据会被发送到其余的 reduce 工作
SET hive.skewjoin.key=100000;

尖叫提醒

数据歪斜在 group by 的状况下也会产生,所以能够开启一个配置:set hive.groupby.skewindata=true,优化 group by 呈现的数据歪斜,一旦开启之后,执行作业时会首先额定触发一个 mr 作业,该作业的 map 工作的输入会被随机地调配到 reduce 工作上,从而防止数据歪斜

执行引擎

Hive 反对多种执行引擎,比方 spark、tez。对于执行引擎的抉择,会影响整体的查问性能。应用的配置如下:

SET hive.execution.engine=<engine>; -- <engine> = mr|tez|spark
  • mr: 默认的执行引擎,在 Hive2.0 版本版本中被标记过期
  • tez: 能够将多个有依赖的作业转换为一个作业,这样只需写一次 HDFS,且两头节点较少,从而大大晋升作业的计算性能。
  • spark: 一个通用的大数据计算框架,基于内存计算,速度较快

优化器

与关系型数据库相似,Hive 会在真正执行计算之前,生成和优化逻辑执行打算与物理执行打算。Hive 有两种优化器:Vectorize(向量化优化器)Cost-Based Optimization (CBO, 老本优化器)

向量化优化器

向量化优化器会同时解决大批量的数据,而不是一行一行地解决。要应用这种向量化的操作,要求表的文件格式为 ORC,配置如下:

SET hive.vectorized.execution.enabled=true; -- 默认 false

老本优化器

Hive 的 CBO 是基于 apache Calcite 的,Hive 的 CBO 通过查问老本 (有 analyze 收集的统计信息) 会生成有效率的执行打算,最终会缩小执行的工夫和资源的利用,应用 CBO 的配置如下:

SET hive.cbo.enable=true; -- 从 v0.14.0 默认 true
SET hive.compute.query.using.stats=true; -- 默认 false
SET hive.stats.fetch.column.stats=true; -- 默认 false
SET hive.stats.fetch.partition.stats=true; -- 默认 true

总结

本文次要介绍了 Hive 调优的基本思路。总共分为四局部,首先介绍了调优的根本工具应用(explain、analyze); 接着从表设计层面介绍了一些优化策略(分区、分桶、索引);而后介绍了数据存储方面的优化(文件格式、压缩、存储优化);最初从作业层面介绍了优化的技巧(开启本地模式、JVM 重用、并行执行、fetch 模式、Join 优化、执行引擎与优化器)。本文次要为 Hive 性能调优提供一些思路,在理论的操作过程中须要具体问题具体分析。总之一句话,重剑无锋,为作业调配正当的资源基本上能够满足大部分的状况,适宜的就是最好的,没有必要谋求狂拽酷炫的技巧,应该把更多的精力放在业务问题上,因为工具的存在的价值是为了解决业务问题的,切不可轻重倒置。

公众号『大数据技术与数仓』,回复『材料』支付大数据资料包

正文完
 0