Hive调优总结

熟悉Hive的开发者都知道,Hive会将操作语句转换为MapReduce作业,对于开发者是透明的,我们并不需要理解其原理就可以专注手头的工作,但是在实际开发当中数据量巨大,我们需要采取一些优化措施来提高MR作业的效率。

1.执行计划

与关系型数据库一样,我们可以使用EXPLAIN命令来分析HQL语句的执行流程,这也是分析一条SQL语句的瓶颈与性能不可缺少的一个步骤。

对于Hive的有些HQL语句会转换为MR作业去执行,而这些Job会被划分为多个stage来执行,一般的,一个Hive任务会包含有一个或者多个stage阶段,不同的stage间会存在着数据依赖,越复杂的查询语句就会引入越多的stage,因此stage越多就需要越多的时间去执行。一个stage可以是一个MR作业,也可以是一个抽样阶段,或者是一个合并阶段,也可以是一个limit阶段,默认的Hive一次只会执行一个stage

在一个复杂的SQL语句中,会有多个stage,它们之间会有依赖关系,我们可以将它们之间的依赖关系看做一个DAG图。以insert into table page_view_orc_snappy select * from page_view;语句为例:

hive (db_hive)> explain insert into table page_view_orc_snappy select * from page_view;
OK
Explain
STAGE DEPENDENCIES:
  Stage-1 is a root stage
  Stage-7 depends on stages: Stage-1 , consists of Stage-4, Stage-3, Stage-5
  Stage-4
  Stage-0 depends on stages: Stage-4, Stage-3, Stage-6
  Stage-2 depends on stages: Stage-0
  Stage-3
  Stage-5
  Stage-6 depends on stages: Stage-5

STAGE PLANS:
  Stage: Stage-1
    Map Reduce
      Map Operator Tree:
          TableScan
            alias: page_view
            Statistics: Num rows: 32450 Data size: 35695344 Basic stats: COMPLETE Column stats: NONE
            Select Operator
              expressions: user_ip (type: string), username (type: string), user_time (type: string), request_url (type: string), request_state (type: string), request_port (type: string), limited (type: string), des_url (type: string), brower (type: string), brower_limit (type: string), to_url (type: string)
              outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, _col6, _col7, _col8, _col9, _col10
              Statistics: Num rows: 32450 Data size: 35695344 Basic stats: COMPLETE Column stats: NONE
              File Output Operator
                compressed: false
                Statistics: Num rows: 32450 Data size: 35695344 Basic stats: COMPLETE Column stats: NONE
                table:
                    input format: org.apache.hadoop.hive.ql.io.orc.OrcInputFormat
                    output format: org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
                    serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde
                    name: db_hive.page_view_orc_snappy

  Stage: Stage-7
    Conditional Operator

  Stage: Stage-4
    Move Operator
      files:
          hdfs directory: true
          destination: hdfs://hadoop-senior.shinelon.com:8020/tmp/hive-root/hive_2019-05-05_16-38-06_339_7245769519280809489-1/-ext-10000

  Stage: Stage-0
    Move Operator
      tables:
          replace: false
          table:
              input format: org.apache.hadoop.hive.ql.io.orc.OrcInputFormat
              output format: org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
              serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde
              name: db_hive.page_view_orc_snappy

  Stage: Stage-2
    Stats-Aggr Operator

  Stage: Stage-3
    Map Reduce
      Map Operator Tree:
          TableScan
            File Output Operator
              compressed: false
              table:
                  input format: org.apache.hadoop.hive.ql.io.orc.OrcInputFormat
                  output format: org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
                  serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde
                  name: db_hive.page_view_orc_snappy

  Stage: Stage-5
    Map Reduce
      Map Operator Tree:
          TableScan
            File Output Operator
              compressed: false
              table:
                  input format: org.apache.hadoop.hive.ql.io.orc.OrcInputFormat
                  output format: org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat
                  serde: org.apache.hadoop.hive.ql.io.orc.OrcSerde
                  name: db_hive.page_view_orc_snappy

  Stage: Stage-6
    Move Operator
      files:
          hdfs directory: true
          destination: hdfs://hadoop-senior.shinelon.com:8020/tmp/hive-root/hive_2019-05-05_16-38-06_339_7245769519280809489-1/-ext-10000

Time taken: 48.119 seconds, Fetched: 82 row(s)

首先分析它的执行计划,首先我们以一张DAG图来看看它的执行关系:
在这里插入图片描述
从上面的图以及打印的执行计划可以看出,有些stage可以并行执行。默认hive一次只执行一个stage,对于一些stage,它们之间并没有依赖关系,因此,为了减少执行时间,可以并行执行。

2.并行执行

我们可以通过参数hive.exec.parallel值为true,开启并发执行,在共享集群中,如果job中并行执行的阶段增多,那么集群利用率也会增加。可以在hive-site.xml文件中配置下面属性:

<property>
	  <name>hive.exec.parallel</name>
	  <value>true</value>
	  <description>Whether to execute jobs in parallel</description>
	</property>

3.避免一些不必要的MR作业

前面 提到过在hive中有些查询会转换为MR作业,但有的不会,在hive中,我们可以进行优化,让一些语句避免执行MR作业,从而加快效率,这些参数就是hive.fetch.task.conversion,在hive-site.xml中可以设置下面属性:

<property>
  <name>hive.fetch.task.conversion</name>
  <value>minimal</value>
  <description>
    Some select queries can be converted to single FETCH task minimizing latency.
    Currently the query should be single sourced not having any subquery and should not have
    any aggregations or distincts (which incurs RS), lateral views and joins.
    1. minimal : SELECT STAR, FILTER on partition columns, LIMIT only
    2. more    : SELECT, FILTER, LIMIT only (TABLESAMPLE, virtual columns)
  </description>
</property>

可以将该参数的值设置为more来优化sql,在不设置该参数的情况下,查询某一字段会执行mapreduce程序,当设置参数后,就不会走mapreduce程序了。

select empname from emp;查询语句为例,在不设置为more的情况下,其执行过程如下:
在这里插入图片描述
很明显上面执行过程一条简单的语句就需要很长的时间,而且跑的是MR作业,实际上当我们将上述参数设置为more之后就可以避免执行MR作业,之心时间也可以加快很多,调优后执行结果如下图所示:
在这里插入图片描述
对于上面的执行结果,很明显,速度提升了数百倍。

4.join优化

在hive中join操作也很常见,对于join操作,我们需要 掌握一些编写SQL的技巧与调优技能才能使得复杂的语句执行速度不至于很慢。
一般情况,在hive中,当对3个或者更多的表进行join操作的时候,如果每个ON字句中都使用相同的连接键的话,那么只会产生一个MR job。
在多个表join中,如果最小的表在最左端,那么hive也许会将这张表加入分布式缓存中,而不是分发到其他表所在的机器上,这也是map-side join优化,幸运的是,我们可以不用编写时考虑哪张表是小表而调整join顺序,hive提供了hive.auto.convert.join可以来优化,默认是启动的。

<property>
  <name>hive.auto.convert.join</name>
  <value>true</value>
  <description>Whether Hive enables the optimization about converting common join into mapjoin based on the input file size</description>
</property>

那么一张表多大会被hive认为是小表呢?默认的是25000000字节,用户也可以通过下面参数自己设置:

<property>
  <name>hive.mapjoin.smalltable.filesize</name>
  <value>25000000</value>
  <description>The threshold for the input file size of the small tables; if the file size is smaller than this threshold, it will try to convert the common join into map join</description>
</property>

当表中的数据是分桶的时候,那么对于大表,在特定的情况下也可以使用这个优化。简单的说,表中的数据必须是按照ON语句中的键进行分桶的,而且其中一张表的分桶的个数必须是另一张表分桶个数的若干倍,当满足这些条件的时候,hive可以在map阶段按照分桶数据进行连接。在这种情况下,不需要获取到表中的所有数据,之后才去和另一张表中的每个分桶进行匹配连接,这个优化默认不是开启的。需要设置参数hive.enforce.sortmergebucketmapjoin,配置如下:

<property>
  <name>hive.enforce.sortmergebucketmapjoin</name>
  <value>false</value>
  <description>If the user asked for sort-merge bucketed map-side join, and it cannot be performed,
    should the query fail or not ?
  </description>
</property>

如果所涉及的分桶表都具有相同的分桶数,而且数据是按照连接键或桶的键进行排序的,那么这时hive可以执行一个更快的分类-合并连接(sort-merge join),需要设置三个参数hive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormathive.auto.convert.sortmerge.join=truehive.enforce.sortmergebucketmapjoin=true来优化:

<property>
  <name>hive.input.format</name>
  <value>org.apache.hadoop.hive.ql.io.CombineHiveInputFormat</value>
  <description>The default input format. Set this to HiveInputFormat if you encounter problems with CombineHiveInputFormat.</description>
</property>

<property>
  <name>hive.auto.convert.sortmerge.join</name>
  <value>false</value>
  <description>Will the join be automatically converted to a sort-merge join, if the joined tables pass
    the criteria for sort-merge join.
  </description>
</property>

<property>
  <name>hive.enforce.sortmergebucketmapjoin</name>
  <value>false</value>
  <description>If the user asked for sort-merge bucketed map-side join, and it cannot be performed,
    should the query fail or not ?
  </description>
</property>

5.调整mapper和reducer的个数

hive通过将查询划分为一个或者多个MR任务达到并行化的目的,每个任务都可能具有多个mapper和reducer任务,其中一些是可以并行执行的,确定最佳的mapper个数和reducer个数取决于多个变量,例如输入的数据量的大小以及对这些数据操作的类型等。

设置太多的mapper与reducer个数,就会导致启动阶段,调度与运行job的过程中产生过多的开销;如果设置的数量太少,那么就可能没有充分利用好集群的并行性。
hive会根据输入的数据量来分配reducer的个数,我们可以通过参数hive.exec.reducers.bytes.per.reducer来设置每个reducer的数据量大小,默认是1G,将该值调大,可以减少reducer的数量,调小,可以增加 reducer的数量。

<property>
  <name>hive.exec.reducers.bytes.per.reducer</name>
  <value>1000000000</value>
  <description>size per reducer.The default is 1G, i.e if the input size is 10G, it will use 10 reducers.</description>
</property>

有些查询map阶段之后产生的中间数据量要大于输入的数据量,有时候会小于输入的数据量,因此合理的设置reducer的数量也是一个经验挑战,可以通过参数mapred.reduce.tasks,默认是-1,hive会自动计算其个数:

<property>
  <name>mapred.reduce.tasks</name>
  <value>-1</value>
    <description>The default number of reduce tasks per job.  Typically set
  to a prime close to the number of available hosts.  Ignored when
  mapred.job.tracker is "local". Hadoop set this to 1 by default, whereas Hive uses -1 as its default value.
  By setting this property to -1, Hive will automatically figure out what should be the number of reducers.
  </description>
</property>

但是实际生产中,集群中数据量会很大,为了控制资源的利用情况,防止一个job过大,消耗完集群资源,使得其他job无法运行,这时可以通过参数hive.exec.reducers.max来设置最大值:

<property>
  <name>hive.exec.reducers.max</name>
  <value>999</value>
  <description>max number of reducers will be used. If the one
	specified in the configuration parameter mapred.reduce.tasks is
	negative, Hive will use this one as the max number of reducers when
	automatically determine number of reducers.</description>
</property>

对于这个属性的值,一般情况下可以根据一个公式来计算,1.5倍数可以防止未充分利用集群的资源:

(集群总共Reduce槽位的个数*1.5 /(执行中查询的平均个数))

6.JVM重用

众所周知,hadoop生态系统底层是java开发的,也就是说它基于JVM运行,因此我们可以设置JVM重用来优化hadoop和hive。
hadoop默认配置是派生JVM来执行map和reduce任务的,这时JVM的启动可能会造成很大的开销,尤其是执行的job包含上千个task的时候,JVM重用可以使得JVM实例在同一个job中重新使用N次。这个参数可以在hadoop的安装目录下的mapred-site.xml文件中配置:

<property>
  <name>mapred.job.reuse.jvm.num.tasks</name>
  <value>10</value>
</property>

这个参数有一个缺点,开启JVM重用将会一直占用使用到的task插槽,以便进行重用,直到执行结束才会释放,如果某个job中有几个task执行的时间比其他task时间多很多,那么它们会一直保留插槽让其空闲无法被其他job使用,因此浪费了资源。

7.推测执行

推测执行就是当一个任务执行时间过长,mapreduce就会重新启动一个任务去执行该任务,并且判断这两个任务那个先执行完毕,先执行完毕的结果就被作为该任务的执行结果,另一个任务就会被删除。因为推测执行有时候会启动很多任务,占用资源,因此可以在调优时将下面三个参数设置为false,关闭推测执行。不过打开推测执行可以缓解数据倾斜。

在hive-site.xml文件中配置:

<property>
  <name>hive.mapred.reduce.tasks.speculative.execution</name>
  <value>true</value>
  <description>Whether speculative execution for reducers should be turned on. </description>
</property>

在hadoop的mapred-site.xml文件中配置:

<property>
  <name>mapreduce.map.speculative</name>
  <value>true</value>
</property>

<property>
  <name>mapreduce.reduce.speculative</name>
  <value>true</value>
</property>

8.单个mapreduce中多group by

该优化操作可以将多个group by操作组装到单个MR中,通过如下参数设置:

<property>
  <name>hive.multigroupby.singlereducer</name>
  <value>false</value>
  <description>Whether to optimize multi group by query to generate single M/R
  job plan. If the multi group by query has common group by keys, it will be
  optimized to generate single M/R job.</description>
</property>

9.动态分区调整

在hive的使用中,我们进行需要进行分区,但是如果分区过多,那么在系统中就会产生大量的输出控制流,所以太多的分区对系统的性能并不好,通常,我们会设置动态分区模式为严格模式,在严格模式下,必须保证一个分区是静态的,可通过下面 参数来设置:

<property>
  <name>hive.exec.dynamic.partition.mode</name>
  <value>strict</value>
  <description>In strict mode, the user must specify at least one static partition in case the user accidentally overwrites all partitions.</description>
</property>

还可以设置如下参数来设置每个mapper或者reducer可以创建的最大分区数:

<property>
  <name>hive.exec.max.dynamic.partitions.pernode</name>
  <value>100</value>
  <description>Maximum number of dynamic partitions allowed to be created in each mapper/reducer node.</description>
</property>

可以通过下面参数设置一个动态分区语句可以创建的最大动态分区个数:

<property>
  <name>hive.exec.max.dynamic.partitions</name>
  <value>1000</value>
  <description>Maximum number of dynamic partitions allowed to be created in total.</description>
</property>

通过下面参数设置全局创建的最大文件个数:

<property>
  <name>hive.exec.max.created.files</name>
  <value>100000</value>
  <description>Maximum number of HDFS files created by all mappers/reducers in a MapReduce job.</description>
</property>

欢迎加入java大数据交流群:731423890

参考资料:
《Hive编程指南》

  • 11
    点赞
  • 95
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值