首页 > 技术文章 > Hive性能调优的最佳实践

jmx-bigdata 2019-02-28 17:32 原文

目录

1.查询性能的实用工具

1.1EXPLAIN语句

1.1.1EXPLAIN语法

1.1.2查询计划

1.1.3例子

1.2 ANALYZE语句

1.2.1 ANALYZE语法

1.2.2例子

1.2.3查看统计信息

1.3日志

2.表设计调优

2.1分区表

2.2分桶表

2.3创建索引

2.3.1创建索引语法

2.3.2删除索引

3.数据格式调优

3.1文件格式

3.2压缩

4.作业调优

4.1本地模式

4.2设置fetch task

4.3JVM重用

4.4并行执行

4.5JOIN优化

4.5.1普通的join

4.5.2Map Join

4.5.3Bucket map join

4.5.4Skew join

4.6执行引擎

4.7优化器


1.查询性能的实用工具

HQL提供了EXPLAIN和ANALYZE命令语句,可以用来查看某个查询的性能。另外,Hive的日志包含了性能方面和线上故障排查的详细信息,可以结合在一起使用,进行性能调优。

1.1EXPLAIN语句

1.1.1EXPLAIN语法

Hive提供了EXPALIN语句,可以用来查看执行某个查询的执行计划(execution plan)。可以使用该命令进行性能分析。

基本的语法为:

EXPLAIN [FORMATTED|EXTENDED|DEPENDENCY|AUTHORIZATION] hql_query

其中可选的四个关键字的具体含义为:

FORMATTED:返回JSON格式的查询计划

EXTENDED:返回更加详细的额外信息,比如文件的路径名

DEPENDENCY:返回JSON格式的输出,该输出包括查询所依赖的表的信息和分区的信息,从Hive v0.10.0开始,才添加的功能。比如

{"input_tables":[{"tablename":"default@music2","tabletype":"MANAGED_TABLE"}],"input_partitions":[]}

AUTHORIZATION:返回所有需要授权的条目,包括运行查询的输入和输出,如果授权失败,还会列出授权失败的信息,从从Hive v0.14.0开始,才添加的功能,比如:

INPUTS:
  default@music2
OUTPUTS:
  hdfs://kms-1.apache.com:8020/tmp/hive/kms/df84775a-f091-46da-9a76-5721ebb12566/hive_2019-02-28_10-25-56_312_8620977237876835177-1/-mr-10001
CURRENT_USER:
  kms
OPERATION:
  QUERY

1.1.2查询计划

一个典型的查询计划包括下面三部分:

(1)抽象语法树(AST,Abstract Syntax Tree),Hive使用一个叫做ANTLR的解析生成器自动生成HQL的语法树。

(2)Stage依赖(Stage Dependencies): 列出所有的依赖以及stage的个数

(3)Stage 计划(Stage Plans): 包含了运行job的重要的信息,比如Map/Reduce操作和sort排序

1.1.3例子

hive (default)> explain select name ,count(1) from music2 where date_time="2019-2-16" group by name limit 3;

结果输出:

OK
Explain
STAGE DEPENDENCIES:
  Stage-1 is a root stage
  Stage-0 depends on stages: Stage-1

STAGE PLANS:
  Stage: Stage-1
    Map Reduce
      Map Operator Tree:
          TableScan
            alias: music2
            Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE
            Filter Operator
              predicate: (date_time = '2019-2-16') (type: boolean)
              Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE

              Select Operator
                expressions: name (type: string)
                outputColumnNames: _col0
                Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE

                Group By Operator
                  aggregations: count(1)
                  keys: _col0 (type: string)
                  mode: hash
                  outputColumnNames: _col0, _col1
                  Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE

                  Reduce Output Operator
                    key expressions: _col0 (type: string)
                    sort order: +
                    Map-reduce partition columns: _col0 (type: string)
                    Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE

                    TopN Hash Memory Usage: 0.1
                    value expressions: _col1 (type: bigint)
      Reduce Operator Tree:
        Group By Operator
          aggregations: count(VALUE._col0)
          keys: KEY._col0 (type: string)
          mode: mergepartial
          outputColumnNames: _col0, _col1
          Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE
          Limit
            Number of rows: 3
            Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE
            File Output Operator
              compressed: false
              Statistics: Num rows: 1 Data size: 0 Basic stats: PARTIAL Column stats: NONE

              table:
                  input format: org.apache.hadoop.mapred.SequenceFileInputFormat
                  output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
                  serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe


  Stage: Stage-0
    Fetch Operator
      limit: 3
      Processor Tree:
        ListSink


Time taken: 1.188 seconds, Fetched: 55 row(s)

1.2 ANALYZE语句

Hive的统计信息是能够描述更多细节的数据集合,比如行数、文件数、原始数据大小等。统计信息是元数据,保存在metastore数据库中。Hive支持统计表、分区、列的信息,这些统计信息作为Hive基于成本的优化器(Cost-Based Optimizer)的输入,该优化器能够挑选出消耗最低系统资源的执行计划。在Hive v3之后支持自定收集统计信息,也可以使用ANALYZE语句收集表、分区、列的统计信息。对于当hive.stats.autogather=true时,会自动收集表的统计信息,但是,对于一个新表或者分区,只有在执行INSERT OVERWRITE/INTO语句时,才会自动收集统计信息,执行LOAD操作时,不会自动收集统计信息。

1.2.1 ANALYZE语法

ANALYZE TABLE [db_name.]tablename [PARTITION(partcol1[=val1], partcol2[=val2], ...)]  -- (Note: Fully support qualified table name since Hive 1.2.0, see HIVE-10007.)

  COMPUTE STATISTICS
  [FOR COLUMNS]          -- (Note: Hive 0.10.0 and later.)
  [CACHE METADATA]       -- (Note: Hive 2.1.0 and later.)
  [NOSCAN];

说明:

如果用户未指定任何分区规范,则会为表以及所有分区(如果有)收集统计信息。如果指定了某些分区规范,则仅为这些分区收集统计信息。对于分区表而言,收集统计信息时,必须指定分区列字段名。从  Hive 1.2.0开始,Hive在此命令中完全支持限定表名,如果使用非限定表名,则用户只能计算当前数据库下的表的统计信息。指定可选参数NOSCAN时,该命令不会扫描文件,因此它应该很快,它只收集文件数量和物理大小(字节)的统计信息,而不是所有统计信息。

1.2.2例子

(1)非分区表

对于非分区表,可以使用以下命令,收集表的统计数据。

ANALYZE TABLE Table1 COMPUTE STATISTICS;

对于非分区表,可以使用以下命令,收集所有列的列统计信息

ANALYZE TABLE Table1 COMPUTE STATISTICS FOR COLUMNS;

执行结果:

hive (default)> ANALYZE TABLE youtube_orc  COMPUTE STATISTICS;

Table default.youtube_orc stats: [numFiles=8, numRows=189, totalSize=46769, rawDataSize=410237]

OK

youtube_orc.videoid     youtube_orc.uploader    youtube_orc.age youtube_orc.category    youtube_orc.length      youtube_orc.views    youtube_orc.rate        youtube_orc.ratings     youtube_orc.comments    youtube_orc.relatedid

Time taken: 1.264 seconds

(2)分区表

假设表Table1有4个分区:

Partition1:(ds ='2008-04-08',hr = 11)

Partition2:(ds ='2008-04-08',hr = 12)

Partition3:(ds ='2008-04-09',hr = 11)

Partition4:(ds ='2008-04-09',hr = 12)

当使用以下命令时:仅为partition3(ds ='2008-04-09',hr = 11)收集统计信息。

ANALYZE TABLE Table1 PARTITION(ds='2008-04-09', hr=11) COMPUTE STATISTICS;

当使用以下命令时:为partition3的所有列收集列统计信息(ds ='2008-04-09',hr = 11)。这在Hive 0.10.0及更高版本中可用。

ANALYZE TABLE Table1 PARTITION(ds='2008-04-09', hr=11) COMPUTE STATISTICS FOR COLUMNS;

当使用以下命令时:仅收集分区3和4的统计数据(hr = 11和hr = 12)

ANALYZE TABLE Table1 PARTITION(ds='2008-04-09', hr) COMPUTE STATISTICS;

当使用以下命令时:仅为分区3和4(Hive 0.10.0及更高版本)收集所有列的列统计信息。

ANALYZE TABLE Table1 PARTITION(ds='2008-04-09', hr) COMPUTE STATISTICS FOR COLUMNS;

当使用以下命令时:然后收集所有四个分区的统计信息

ANALYZE TABLE Table1 PARTITION(ds, hr) COMPUTE STATISTICS;

当使用以下命令时:为所有四个分区(Hive 0.10.0及更高版本)收集所有列的列统计信息

ANALYZE TABLE Table1 PARTITION(ds, hr) COMPUTE STATISTICS FOR COLUMNS;

1.2.3查看统计信息

DESCRIBE EXTENDED employee_partitioned PARTITION(year=2018, month=12);

DESCRIBE EXTENDED employee;

DESCRIBE FORMATTED employee.name;

1.3日志

通过日志信息可以查看作业或者查询的详细信息,通过检查日志,可以检查运行时出现的问题以及影响性能的问题。主要有两种类型的日志,一种为系统日志,另一种为作业日志。系统日志包含Hive运行时的状态情况,通过{HIVE_HOME}/conf/hive-log4j.properties配置,主要配置属性如下:

## set logger level
hive.root.logger=WARN,DRFA

## set log file path
hive.log.dir=/tmp/${user.name}

## set log file name
hive.log.file=hive.log

可以通过$hive --hiveconf hive.root.logger=DEBUG,console命令设置session级别的日志输出。Job日志包含Job的信息,通常是由yarn管理。可以使用yarn logs -applicationId <application_id>命令查看。

2.表设计调优

2.1分区表

对大表而言,Hive的分区表是提高查询性能的有效方式。对于一个分区裁剪的查询而言,只会加载指定分区(子目录)的数据,所有可以提高查询的效率。分区key的选择是影响性能的重要因素,通常选择低基数的属性,从而避免生成大量的子目录。下面的属性通常会被作为分区key:

(1)日期和时间:使用日期和时间,比如年、月、日、小时作为分区键,关于日期或者时间列,可以是加载数据的日期,业务日期或者运行日期等。

(2)位置:使用国家、地区、城市作为分区键

(3)业务逻辑:使用部门、销售区域、顾客等作为分区键。

创建分区表的例子:

CREATE TABLE page_view(viewTime INT, userid BIGINT,
     page_url STRING, referrer_url STRING,
     ip STRING COMMENT 'IP Address of the User')
 COMMENT 'This is the page view table'
 PARTITIONED BY(dt STRING, country STRING)
 ROW FORMAT DELIMITED<font></font>
 FIELDS TERMINATED BY '\001'
STORED AS SEQUENCEFILE;

2.2分桶表

分桶表是以分开的文件来组织数据的,分桶表可以加快数据采样。如果分桶的字段与join的字段相同,则可以提高join查询的性能。选择合适的分桶字段提高join查询的性能,生产环境中通常使用具有标识的列,该列很有可能作为where过滤条件或者join条件。

创建分桶表的例子:

CREATE TABLE page_view(viewTime INT, userid BIGINT,
     page_url STRING, referrer_url STRING,
     ip STRING COMMENT 'IP Address of the User')
 COMMENT 'This is the page view table'
 PARTITIONED BY(dt STRING, country STRING)
 CLUSTERED BY(userid) SORTED BY(viewTime) INTO 32 BUCKETS
 ROW FORMAT DELIMITED
   FIELDS TERMINATED BY '\001'
   COLLECTION ITEMS TERMINATED BY '\002'
   MAP KEYS TERMINATED BY '\003'
 STORED AS SEQUENCEFILE;

2.3创建索引

Hive支持索引的创建,Hive索引能够提供基于key的数据浏览和更好的数据访问,比如WHERE、GROUP BY、JOIN。使用索引通常比全表扫描更节省开销。

Hive索引的目标是提高表的某些列的查询查询速度。如果没有索引,带有谓词的查询(如'WHERE tab1.col1 = 10')会加载整个表或分区并处理所有行。但是如果col1存在索引,则只需要加载和处理文件的一部分。索引可以提供的查询速度的提高是以创建索引和存储索引的磁盘空间的额外处理为代价的。

2.3.1创建索引语法

CREATE INDEX index_name
  ON TABLE base_table_name (col_name, ...)
  AS index_type
  [WITH DEFERRED REBUILD]
  [IDXPROPERTIES (property_name=property_value, ...)]
  [IN TABLE index_table_name]
  [
     [ ROW FORMAT ...] STORED AS ...
     | STORED BY ...
  ]
  [LOCATION hdfs_path]
  [TBLPROPERTIES (...)]
  [COMMENT "index comment"];

例子:

CREATE INDEX idx_id_employee_id
ON TABLE employee_id (employee_id)
AS 'COMPACT'
WITH DEFERRED REBUILD;


CREATE INDEX idx_gender_employee_id
ON TABLE employee_id (gender_age)
AS 'BITMAP'
WITH DEFERRED REBUILD;

2.3.2删除索引

语法:DROP INDEX [IF EXISTS] index_name ON table_name;

例子:DROP INDEX idx_gender_employee_id ON employee_id;

3.数据格式调优

3.1文件格式

(1)文件格式概述

Hive支持TEXTFILE, SEQUENCEFILE, AVRO, RCFILE, ORC和PARQUET 文件格式。

主要有两种方式指定文件的格式

第一种:CREATE TABLE ... STORE AS <file_format>: 创建时具体指定文件格式

第二种:ALTER TABLE ... [PARTITION partition_spec] SET FILEFORMAT <file_format>: 修改文件的存储格式

当创建表的存储格式是text时,可以使用load命令加载text数据。但是,当将text格式的文件加载到其他存储格式的表时,需要使用INSERT OVERWRITE/INTO TABLE ... SELECT命令,先将数据加载到text存储格式的表中,然后再查询数据,插入其他存储格式的表。可以设置hive.default.fileformat=TextFile(默认)属性,来改变默认的存储格式。

TEXT, SEQUENCE,和AVRO文件都是面向行的存储格式,因此对于不需要全字段扫描的查询而言,不是最优的存储格式。因为即便是查询一列数据,这些存储格式的表也会读取一整行的数据。对于混合的面向行的存储格式而言,比如RCFILE、 ORC、PARQUET,可以解决上述的问题。

TEXTFILE

这是用于创建表的默认文件格式。文本文件是自然可分割的,可以并行处理。它也可以用算法进行压缩,例如gzip、lzo和Snappy。但是,大多数压缩文件不能并行拆分处理。因此,他们只使用一个作业和一个mapper来处理数据,处理速度很慢。使用压缩文本文件的最佳实践是确保文件不太大,接近几个HDFS块大小。

SEQUENCEFILE

以键/值的形式进行二进制存储格式,比文本文件更紧凑,适合MapReduce输出格式。序列文件可以压缩成记录或块级别,其中块级别具有更好的压缩比。

AVRO

是一种二进制格式。不仅如此,它还是一个系列化和反序列化框架。AVRO提供了一个描述数据结构的schema,schema与数据一起存储处理。

RCFILE:

是一种二进制文件,表示按列记录文件。RCFile除了按列的方式存储数据以外,其他方面和序列化文件类似。RCFile把表分成行分片,在每一个分片中先存所有行的第一列,再存第二列,依次类推。

ORC:

这是Optimized Row Columnar缩写。从Hive V0.11开始支持的。ORC格式可以被视为RCFILE的改进版本。它默认情况下,提供更大的块大小256 MB(rcfile有4 MB和sequencefile有1 MB),针对hdfs上的大规模顺序读取进行了优化,支持较大的吞吐量和较少的文件,以减少名称节点中的过载。RCFILE依赖于元存储来了解数据类型,而ORC文件通过使用特定的编码器了解数据类型,根据不同类型以便优化压缩。它还存储基本统计信息,例如列上的最小值、最大值、总和和计数,以及可以用于跳过不重要的行块

PARQUET:

是另一种行列文件格式,其设计与ORC相似。更重要的是,在Hadoop生态中,parquet格式支持的最广泛,而ORC主要支持hive、pig和spark。在v0.13.0之后得到了本地支持。

(2)文件格式选取的最佳实践

根据使用的技术栈,如果主要使用Hive来定义和处理数据,那么建议使用ORC格式。如果还是用hadoop生态的其他技术,考虑到适用性,建议使用parquet格式。

3.2压缩

Hive通过适当压缩在mapper和reducer之间的中间和最终输出数据,可以显著减少数据传输量。因此,查询将具有更好的性能。压缩中间文件在多个MapReduce作业之间生成,我们需要设置hive.exec.compress.intermediate 属性(默认false),在命令行会话或hive-site.xml文件中进行配置。

(1)压缩格式

deflate(.deflate)是一个默认的编解码器,具有平衡的压缩比和CPU消耗。gzip的压缩比非常高,CPU成本也很高。bzip2是可拆分的。当涉及到CPU消耗和压缩比的平衡时,LZ4或Snappy会比deflate要好,但Snappy更受欢迎。因为大多数压缩文件不可拆分,不建议压缩单个大文件。最佳做法是在几个HDFS块大小中生成压缩文件,这样每个文件花费的处理时间就更少了。压缩编解码器可以在mapred-site.xml、hive-site.xml中指定,也可以在命令行session中指定。比如

SET hive.intermediate.compression.codec=
org.apache.hadoop.io.compress.SnappyCodec

当指定hive.exec.compress.output(默认为false)为true时,需要设置mapreduce.output.fileoutputformat.compress.codec指定编码,比如

mapreduce.output.fileoutputformat.compress.codec=
org.apache.hadoop.io.compress.SnappyCodec

4.作业调优

4.1本地模式

在多数情况下,需要使用完全分布式模式处理数据。但当处理的数据较少时,使用完全分布式处理数据,启动完全分布式模式所花费的时间会比作业处理的时间还要多。因此从hive V0.7.0开始,支持本地模式执行作业。

主要的配置:

> SET hive.exec.mode.local.auto=true; -- default false
> SET hive.exec.mode.local.auto.inputbytes.max=50000000;--default 134217728,即128M
> SET hive.exec.mode.local.auto.input.files.max=5; -- default 4

当开启了本地模式时,需要满足下面的条件,才会触发本地模式:

(1)输入数据的大小要小于hive.exec.mode.local.auto.inputbytes.max的值

(2)map task的数量小于hive.exec.mode.local.auto.input.files.max的值

(3)reduce task的数量为1或者0

4.2设置fetch task

启用了fetch ask之后,会对一些简单的查询不启用mapreduce作业。 新的hive版本中hive.fetch.task.conversion默认为more,表示在全表扫描、字段查找、limit查找都不会启用mapreduce作业。

4.3JVM重用

JVM 重用是 Hadoop 调优参数的内容,其对 Hive 的性能具有非常大的影响,特别是对于很难避免小文件的场景或 task 特别多的场景,这类场景大多数执行时间都很短。Hadoop 的默认配置通常是使用派生 JVM 来执行 map 和 Reduce 任务的。这时 JVM 的启动过程可能会造成相当大的开销,尤其是执行的 job包含有成百上千task任务的情况。JVM重用可以使得 JVM 实例在同一个 job 中重新使用 N 次。N 的值可以在 Hadoop 的mapred-site.xml 文件中进行配置。通常在 10-20 之间,具体多少需要根据具体业务场景测试得出。这个功能的缺点是,开启 JVM 重用将一直占用使用到的 task 插槽,以便进行重用,直到任务完成后才能释放。如果某个“不平衡的”job 中有某几个 reduce task 执行的时间要比他 Reduce task 消耗的时间多的多的话,那么保留的插槽就会一直空闲着却无法被其他的 job使用,直到所有的 task 都结束了才会释放。

开启JVM重用,可以设置一个job中进行JVM重用的task最大数量,默认是1,如果设为-1,则表示没有限制。

比如:SET mapreduce.job.jvm.numtasks=5;

4.4并行执行

Hive 会将一个查询转化成一个或者多个阶段。这样的阶段可以是 MapReduce 阶段、抽样阶段、合并阶段、limit 阶段。或者 Hive 执行过程中可能需要的其他阶段。默认情况下,Hive 一次只会执行一个阶段。不过,某个特定的 job 可能包含众多的阶段,而这些阶段可能并非完全互相依赖的,也就是说有些阶段是可以并行执行的,这样可能使得整个 job 的执行时间缩短。不过,如果有更多的阶段可以并行执行,那么 job 可能就越快完成。通过设置参数 hive.exec.parallel 值为 true,就可以开启并发执行。不过,在共享集群中,

需要注意下,如果 job 中并行阶段增多,那么集群利用率就会增加。

set hive.exec.parallel=true; --打开任务并行执行,默认为false
set hive.exec.parallel.thread.number=16; //同一个 sql 允许最大并行度,默认为 8。

4.5JOIN优化

4.5.1普通的join

普通的join又称为reduce端join,是HQL基本的join,通常耗费时间较长,对于普通的join,将大表放在右边,小表放在左边。即小表join大表。

4.5.2Map Join

Mapjoin是指当要join的某张表非常小,可以将该小表加载到内存中,这样可以加快处理的速度,但是会受到表大小的限制。从Hive v0.7.0开始,可以设置自动进行map join。

开启 MapJoin 参数设置:

(1)设置自动选择 Mapjoin

      set hive.auto.convert.join = true; 默认为 true

(2)小表的大小设置(默认 25M 一下认为是小表):

     set hive.mapjoin.smalltable.filesize=600000000 ;

 (3)设置不需要map join hint

                SET hive.auto.convert.join.noconditionaltask=true; -- 默认 true

               SET hive.auto.convert.join.noconditionaltask.size=10000000; 默认10000000

一旦开启了join的自动转换,Hive就会自动检查小表的大小是否大于hive.mapjoin.smalltable.filesize的值,如果大于,则会使用普通的join。如果小于此值,则会使用map join。另外,只要开启了join的自动转换,就不需要写map join提示。

4.5.3Bucket map join

对于分桶表可以使用bucket map join,启用bucket map join,需要设置一下属性:

(1)设置自动选择 Mapjoin

               SET hive.auto.convert.join=true;

(2)开启bucket map join

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

在bucket map join中,所有的表必须是分桶表,而且分桶的字段与join的字段必须一致。另外,大表的分桶数量要是小表分桶数量的倍数。

4.5.4Skew join

当处理的数据分布不均匀时,会发生数据倾斜,即少数的计算节点要处理大量的计算数据,开启下面的属性,会对数据倾斜作出优化。

(1)如果存在数据倾斜,设置为true,默认为false

                 SET hive.optimize.skewjoin=true;

(2)hive 在运行的时候没有办法判断哪个key 会产生多大的倾斜,所以使用这个参数控制倾斜的阈值,如果超过这个值,新的值会发送给其他未使用的reduce。默认值为100000

                     SET hive.skewjoin.key=100000;

4.6执行引擎

Hive支持不同的执行引擎,执行引擎的选择会影响性能。相对其他的配置而言,更换执行引擎的改变是最大的。可以通过下面的命令更换执行引擎

          SET hive.execution.engine=<engine>; -- <engine> = mr|tez|spark

mr:默认的执行引擎,Hive2.0.0已经过时

4.7优化器

Hive在提交一个最终的执行之前,会生成和优化查询的逻辑执行计划于物理执行计划。主要包括两种优化器,一种是Vectorize ,另一种是Cost-Based Optimization (CBO)。

Vectorization优化器

SET hive.vectorized.execution.enabled=true; -- default false

Cost-based 优化器

使用 CBO, 设置以下属性:

SET hive.cbo.enable=true; -- default true after v0.14.0

SET hive.compute.query.using.stats=true; -- default false

SET hive.stats.fetch.column.stats=true; -- default false

SET hive.stats.fetch.partition.stats=true; -- default true

参考:

https://issues.apache.org/jira/browse/HIVE-11160

https://cwiki.apache.org/confluence/display/Hive/StatsDev

Apache Hive Essentials Second Edition

推荐阅读