Hive调优之MapReduce详解

MapReduce整体处理过程

MapReduce是一种计算引擎,也是一种编程模型。MapReduce提供了两个编程接口,即Map和Reduce,让用户能够在此基础上编写自己的业务代码,而不用关心整个分布式计算框架的背后工作。这样能够让开发人员专注自己的业务领域,但如果发生Map/Reduce业务代码以外的性能问题,开发人员通常束手无策。 

 MapReduce会经历作业输入(Input)、业务处理接口Map、Map到Reduce之间数据传输的环节Shuffle、业务处理接口Reduce和作业输出(Output)五大环节。这5个环节还可以进一步分解成如下图:

InputFormat在Hive中的使用

在Hive调优的过程中,我们不希望产生太多 的Map,而把计算任务的等待时间都耗费在Map的启动上;或者不希望生成太多的Map对某个文件进行操作,以免引起资源的争用。因此需要对Map进行控制(在Hive配置“set mapred.map.tasks=task数量”无法控制Map的任务数)需要用算法计算进行调节,具体如下:

  1. 在默认情况下Map的个数defaultNum=目标文件或数据的总大小totalSize/hdfs集群文件块的大小blockSize。
  2. 当用户指定mapred.map.tasks,即为用户期望的Map大小,用expNum表示,这个期望值计算引擎不会立即采纳,它会获取mapred.map.tasks与defaultNum的较大值,用expMaxNum表示,作为待定选项。
  3. 获取文件分片的大小和分片个数,分片大小为参数mapred.min.split.size和blockSize间的较大值,用splitMaxSize表示,将目标文件或数据的总大小除以splitMaxSize即为真实的分片个数,用realSplitNum表示。
  4. 获取realSplitNum与expMaxNum较小值则为实际的Map个数。

defaultNum=totalSize/blockSize
expNum=mapred.map.tasks
expMaxNum=max(expNum,defaultNum)
splitMaxSize=totalSize/max(mapred.min.split.size,blockSize)

实际的map个数=min(realSplitNum, expMaxNum)

通过以上逻辑可以看出:

减少Map个数,需要增大mapred.min.split.size的值,减少mapred.map.tasks的值;

增大Map个数,需要减少mapred.min.split.size的值,同时增大mapred.map.tasks的值。

MapReduce的Mapper类

Mapper的核心Map方法是MapReduce提供给用户编写业务的主要接口之一,它的输入是一个键-值对的形式,输出也是一个键-值形式。

Mapper类其实只是简单定义业务处理的输入和输出规范,具体的逻辑则交给开发者自己去开发。
一个完整的MapReduce任务提交到Hadoop集群,Mapper中的逻辑会被分发到集群中的各个节点,并读取该节点的本地数据(在特殊情况下一个节点的Mapper方法可能读取其他节点的数)进行处理,最后写入到本地。这种模式就是所谓的不移动数据,而只移动计算逻辑的模式。目前绝大部分的分布式计算引擎,相比于移动计算,移动数据需要消耗更多的网络I/O和磁盘I/O的资源。在进行调优时,我们应借鉴这种设计方法,尽可能地减少数据在节点之间的传输。

MapReduce的Reducer类

Reducer的核心Reduce方法是MapReduce提供给用户编写业务的另一个主要接口,它的输入是一个键-数组的形式,和Mapper的输入不太一样。 Reducer任务启动时会去拉取Map写入HDFS的数据,并按相同的键划分到各个Reducer任务中,相同键的值则会存入到一个集合容器中,因此Reducer的输入键所对应的值是一个数组,Reducer输出则是同Mapper一样的键-值对形式。

一个完整的MapReduce任务提交到Hadoop集群,Reducer中的逻辑会被分发到集群中的各个节点。但是不同于Mapper读取本地文件,Reducer会去拉取远程Map节点产生的数据,这里必然也会涉及网络I/O和磁盘I/O。从这里我们可以看到,如非需要对数据进行全局处理,例如全局排序,就关掉Reduce阶段的操作,可以提升程序性能。

MapReduce的Shuffle

Shuffle过程按官方文档的解释是指代从Mapper的输出到Reduer输入的整个过程。但个人认为Shuffle的过程应该是从Mapper的Map方法输出到Reducer的Reduce方法输入的过程。它是制约MapReducer引擎性能的一个关键环节,但同时也保证了Hadoop即使能在一些廉价配置较低的服务器上可靠运行的一个环节。
在Mapper的Map方法中,context.write()方法会将数据计算所在分区后写入到内存缓冲区,缓冲区的大小为mapreduce.task.io.sort.mb=100MB。当缓冲区缓存的数据达到一定的阀值mapreduce.map.sort.spill.percent=0.8,即总缓冲区的80%时,将会启动新的线程将数据写入到HDFS临时目录中。这样设计的目的在于避免单行数据频繁写,以减轻磁盘的负载。这与关系型
数据库提倡批量提交(commit)有相同的作用。
在写入到HDFS的过程中,为了下游Reducer任务顺序快速拉取数据,会将数据进行排序后再写入临时文件中,当整个Map执行结束后会将临时文件归并成一个文件。如果不进行文件的排序归并,意味着下游Reducer任务拉取数据会频繁地搜索磁盘,即将顺序读变为随机读,这会对磁盘I/O产生极大的负载。

Reducer任务启动后,会启动拉取数据的线程,从HDFS拉取所需要的数据。为什么不选用Mapper任务结束后直接推送到Reducer节点,这样可以节省写入到磁盘的操作,效率更高?因为采用缓存到HDFS,让Reducer主动来拉,当一个Reducer任务因为一些其他原因导致异常结束时,再启动一个新的Reducer依然能够读取到正确的数据。
从HDFS拉取的数据,会先缓存到内存缓冲区,当数据达到一定的阈值后会将内存中的数据写入内存或者磁盘中的文件里。当从HDFS拉取的数据能被预先分配的内存所容纳,数据会将内存缓存溢出的数据写入该内存中,当拉取的数据足够大时则会将数据写入文件,文件数量达到一定量级进行归并。

MapReduce的Map端聚合Combiner类

MapReduce的Map端聚合通常指代实现Combiner类。Combiner也是处理数据聚合,但不同于Reduce是聚合集群的全局数据。Combiner聚合是Map阶段处理后的数据,因此也被称之为Map的聚合。使用Combiner的初衷是减少Shuffle过程的数据量,减轻系统的磁盘和网络的压力。

 

Map端的聚合与Hive配置 

在Hive中也可以启用Map端的聚合,但有别于使用Combiner,Hive端的聚合更多的是使用哈希表。即在Map执行时启用hash表用来缓存数据,并聚合数据,而不是单独启用Combiner任务来完成聚合。下面是关于Map端聚合的一些配置:

hive.map.aggr:默认值为true,表示开启Map端的聚合。

通常使用Map聚合往往是为了减少Map任务的输出,减少传输到下游任务的Shuffle数据量,但如果数据经过聚合后不能明显减少,那无疑就是浪费机器的I/O资源。
因此Hive在默认开启hive.map.aggr的同时,引入了两个参数,
hive.map.aggr.hash.min.reduction和hive.groupby.mapaggr.checkinterval,用于控制何时启用聚合。

(1)    hive.map.aggr.hash.min.reduction:是一个阈值,默认值是0.5。
(2)    hive.groupby.mapaggr.checkinterval:默认值是100000。Hive在启用Combiner时会尝试取这个配置对应的数据量进行聚合,将聚合后的数据除以聚合前的数据,如果小于hive.map.aggr.hash.min.reduction会自动关闭。

hive.map.aggr.hash.percentmemory:默认值是0.5。该值表示在进行Mapper端的聚合运行占用的最大内存。例如,分配给该节点的最大堆(xmx)为1024MB,那么聚合所能使用的最大Hash表内存是512MB,如果资源较为宽裕,可以适当调节这个参数。
hive.map.aggr.hash.force.flush.memory.threshold:默认值是0.9。该值表示当在聚合时,所占用的Hash表内存超过0.9,将触发Hash表刷写磁盘的操作。例如Hash表内存是512MB,当Hash表的数据内存超过461MB时将触发Hash表写入到磁盘的操作。

Hive配置与作业输出 

Hive提供了作业输出文件的压缩,可以减少在Shuffle过程的数据量,减轻磁盘和网络的负载。但是有压缩就会有解压缩,免不了性能损耗,一般在大型作业中才会开启文件作业的压缩。开启文件作业的压缩只要将hive.exec.compress.intermediate参数设置为true。当然Hive提供写入到最终Hive表或者HDFS文件的压缩参数--hive.exec.compress.output。
上述的压缩如果要是MapReduce中起作用的前提是需要配置mapred.output.compression.codec和mapred.output.compression两个属性。

Hive提供多种方式去合并执行过程中产生的小文件,如下:

启用hive.merge.mapfile参数,默认启用,合并只有Map任务作业的输出文件;

启用hive.merge.mapredfiles参数,默认启用,合并MapReduce作业最终的输出文件;

设置hive.merge.smallfiles.avgsize参数,默认16MB,当输出的文件小于该值时,启用一个MapReduce任务合并小文件;

设置hive.merge.size.per.task参数,默认256MB,是每个任务合并后文件的大小。一般设置为和HDFS集群的文件块大小一致。

MapReduce作业与Hive配置 

Hive的配置除了能控制作业在MapReduce中每个阶段的运行外,也能用于控制整个作业的运行模式。下面是一些较为常见的通过Hive配置操作MapReduce作业运行的配置:

hive.optimize.countdistinct:默认值为true,Hive 3.0新增的配置项。当开启该配置项时,去重并计数的作业会分成两个作业来处理这类SQL,以达到减缓SQL的数据倾斜作用。hive.exec.parallel:默认值是False,是否开启作业的并行。默认情况下,如果一个SQL被拆分成两个阶段,如stage1、stage2,假设这两个stage没有直接的依赖关系,还是会采用窜行的方式依次执行两个阶段。如果开启该配置,则会同时执行两个阶段。在资源较为充足的情况下开启该配置可以有效节省作业的运行时间。

hive.exec.parallel.thread.num:默认值是8,表示一个作业最多允许8个作业同时并行执行。

hive.exec.mode.local.auto:默认值是false,表示是否开启本地的执行模式。开启该配置表示Hive会在单台机器上处理完所有的任务,对于处理数据量较少的任务可以有效地节省时间。开启本地模式还需要以下几个配置帮助。

hive.exec.mode.local.auto.inputbytes.max:默认值134217728(128MB),表示作业处理的数据量要小于该值,本地模式。

hive.exec.mode.local.auto.tasks.max:默认值是4,表示作业启动的任务数必须小于或者等于该值,本地模式才能生效。在Hive 0.9的版本以后该配置hive.exec.mode.local.auto.input.files.max配置所取代,其含义和被
hive.exec.mode.local.auto.tasks.max相同。

hive.optimize.correlation:默认值为false,这个配置我们称之为相关性优化,打开该配置可以减少重复的Shuffle操作。

SELECT t1.key, sum(value) FROM t1 JOIN t2 ON (t1.key = t2.key) GROUP BY t1.key

上面的SQL在执行时,JOIN操作和GROUP BY操作通常情况下都会产生Shuffle的操作,但是由于Join阶段操作后输出的结果是作为GROUP BY阶段操作的输入,并且在JOIN操作时,数据已经按t1.Key分区,因此通常情况下GROUP BY操作没有必要为t1.key进行重新Shuffle。然而在一般情况下,Hive并不知道JOIN操作和GROUP BY操作之间的这种相关性,因此会产两个Shuffle操作。这种情况往往会导致低效SQL的产生,通过开启hive.optimize.correlation配置,可以避免这种低效SQL的产生。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值