Hive优化-参数调优

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


  1. 启用本地模式
    对于数据量比较小的操作,可以使用本地模式提交任务。因为在这种情况下,集群执行的话为查询发出执行任务的时间消耗可能会比实际job的执行执行时间要多的多
#默认是false
hive> set hive.exec.mode.local.auto=true
  1. 并行执行
    当一个sql中有多个job时候,且这多个job之间没有依赖,则可以让顺序执行变为并行执行
// 开启任务并行执行
hive> set hive.exec.parallel=true;
// 同一个sql允许并行任务的最大线程数 
hive> set hive.exec.parallel.thread.number=8;
  1. 设置hive执行模式
hive> set hive.mapred.mode
hive.mapred.mode=nonstrict

如果设置为严格(strict)模式,则hive作业禁止3种类型查询:

  • 分区表没有启用分区过滤字段
  • order by没有指定limit限制
  • 笛卡尔积
  1. 调整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>
</property>

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

<property>
  <name>mapred.reduce.tasks</name>
  <value>-1</value>
</property>

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

<property>
  <name>hive.exec.reducers.max</name>
  <value>999</value>
</property>
  1. 合并小文件
    小文件的产生有三个地方,map输入,map输出,reduce输出,小文件过多也会影响hive的分析效率:
    设置map输入的小文件合并
set mapred.max.split.size=256000000;  
//一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)
set mapred.min.split.size.per.node=100000000;
//一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)  
set mapred.min.split.size.per.rack=100000000;
//执行Map前进行小文件合并
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

设置map输出和reduce输出进行合并的相关参数:

//设置map端输出进行合并,默认为true
set hive.merge.mapfiles = true
//设置reduce端输出进行合并,默认为false
set hive.merge.mapredfiles = true
//设置合并文件的大小
set hive.merge.size.per.task = 256*1000*1000
//当输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件merge。
set hive.merge.smallfiles.avgsize=16000000
  1. jvm重用
    jvm重用是hadoop调优参数的内容,其对hive的性能影响是非常大的,特别是对于针对很多小文件的场景或task特别多的场景,这类场景任务执行的时间都很短。hadoop默认使用派生的jvm执行mapreduce任务,对于jvm的启动时很大的开销,特别针对于task任务比较多的场景。
    jvm重用可以使jvm实例在同一个job中运行n次,n的值在hadoop的mapred-site.xml文件进行配置:
<property>
<name> mapred.job.reuse.jvm.num.tasks </name>
<value>10</value>
</property>

也可以在hive cli中通过set设置:

hive (default)> set mapred.job.reuse.jvm.num.tasks;
mapred.job.reuse.jvm.num.tasks=1

hive (default)> set mapred.job.reuse.jvm.num.tasks=10;

JVM的一个缺点是,开启JVM重用将会一直占用使用到的task插槽,以便进行重用,直到任务完成后才能释放。如果某个“不平衡“的job中有几个 reduce task 执行的时间要比其他reduce task消耗的时间多得多的话,那么保留的插槽就会一直空闲着却无法被其他的job使用,直到所有的task都结束了才会释放。

  1. 推测执行
    所谓的推测执行,就是当所有task都开始运行之后,Job Tracker会统计所有任务的平均进度,如果某个task所在的task node机器配置比较低或者CPU load很高(原因很多),导致任务执行比总体任务的平均执行要慢,此时Job Tracker会启动一个新的任务(duplicate task),原有任务和新任务哪个先执行完就把另外一个kill掉
    hadoop的推测执行功能由mapred-site.xml文件中的2个参数决定:
<property>
<name> mapred.map.tasks.speculative.execution </name>
<value>true</value>
</property>

<property>
<name> mapred.reduce.tasks.speculative.execution</name>
<value>true</value>
</property>

hive本身也有控制推测执行的参数,可以在hive-site.xml文件中配置:

<property>
<name>hive.mapred.reduce.tasks.speculative.execution </name>
<value>true</value>
</property>

hive中推测执行参数默认值如下:

hive (default)> set mapred.map.tasks.speculative.execution;
mapred.map.tasks.speculative.execution=true

hive (default)> set mapred.reduce.tasks.speculative.execution;
mapred.reduce.tasks.speculative.execution=true

hive (default)> set hive.mapred.reduce.tasks.speculative.execution;
hive.mapred.reduce.tasks.speculative.execution=true
  1. 单个mapreduce中运行多个group by
    参数hive.multigroupby.singlemr控制将查询中的多个group by组装到单个mapreduce任务中。
<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>
  1. 聚合优化
    这个设置可以将顶层的聚合操作放在Map阶段执行,从而减轻清洗阶段数据传输和Reduce阶段的执行时间,提升总体性能。
hive.map.aggr=true
  1. hive.fetch.task.conversion的调优
#默认值,查询都要经过mapreduce
hive> hive.fetch.task.conversion=minimal

#建议值,hive中如果是简单查询,没有函数,orderby等,语句会从hdfs直接查询不会转成mapreduce
hive> set hive.fetch.task.conversion=more
  1. 动态分区调整
    在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>