目录
map数量的控制
减少Map个数
增大Map个数
Shuffle过程分析
Map端聚合
Combiner类
目的
Map端的聚合与Hive配置
注意事项
当我们提交一个mr任务会启用多少个map呢,这个map的数量如何控制呢,如何调整map的数量
在调优时我们不希望生成太多的Map,而把计算任务的等待时间都耗费在Map的启动上;或者不希望生成太多的Map对某个文件进行操作,以免引起资源的争用。这时候就需要对Map进行控制。
在Hive中配置“set mapred.map.tasks=task数量”无法控制Map的任务数,调节Map任务数需要一套算法,该算法也和InputFormat有密切的关系,具体如下:
上述算法用代码表达如下:
defaultNum=totalSize/blockSize
expNum=mapred.map.tasks
expMaxNum=max(expNum,defaultNum)
realSplitNum=totalSize/max(mapred.min.split.size,blockSize)
实际的map个数=min(realSplitNum, expMaxNum)
通过上面的逻辑知道:
需要增大mapred.min.split.size的值,减少mapred.map.tasks的值;
需要减少mapred.min.split.size的值,同时增大mapred.map.tasks的值。
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类。Combiner也是处理数据聚合,但不同于Reduce是聚合集群的全局数据。Combiner聚合是Map阶段处理后的数据,因此也被称之为Map的聚合。
ombiner是MapReduce计算引擎提供的另外一个可以供用户编程的接口。Combiner所做的事情如果不额外进行编写,则可以直接使用Reducer的逻辑,但是Combiner发生的地方是在Mapper任务所在的服务器,因此它只对本地的Mapper任务做Reducer程序逻辑里面的事情,无法对全局的Mapper任务做,所以一般也被称为Map端的Reducer任务。如图5.10所示为Combiner的使用方法示例图。
使用Combiner的初衷是减少Shuffle过程的数据量,减轻系统的磁盘和网络的压力。如图5.11所示为使用Combiner缩小数据的表示图。
在Hive中也可以启用Map端的聚合,但有别于使用Combiner,Hive端的聚合更多的是使用哈希表。即在Map执行时启用hash表用来缓存数据,并聚合数据,而不是单独启用Combiner任务来完成聚合。下面是关于Map端聚合的一些配置。
hive.map.aggr:默认值为true,表示开启Map端的聚合。开启和不开启Map端聚合的差别可以在执行计划中看到,详见下面两个案例执行计划的精简结构。
未启用Map端聚合的执行计划的精简结构如下:
explain
select s_age,count(1) from student_tb_orc
group by s_age;
STAGE PLANS:
Stage: Stage-1
Map Reduce
//以下描述的是Map阶段的主要操作
Map Operator Tree:
//读表操作
TableScan
//字段过滤
Select Operator
Reduce Output Operator
//以下描述的是Reduce阶段的主要操作
Reduce Operator Tree:
//分组聚合
Group By Operator
File Output Operator
启用Map端聚合的执行计划的精简结构如下:
set hive.map.aggr=true;
explain
select s_age,count(1) from student_tb_orc
group by s_age;
STAGE PLANS:
Stage: Stage-1
Map Reduce
Map Operator Tree:
TableScan
Select Operator
//Map端的分组聚合,该操作是区分是否开启Map的聚合的区别
Group By Operator
Reduce Output Operator
Reduce Operator Tree:
Group By Operator
File Output Operator
从上面的案例可以看到,启用Combiner,会在Map阶段多出一个GroupBy操作。
通常使用Map聚合往往是为了减少Map任务的输出,减少传输到下游任务的Shuffle数据量,但如果数据经过聚合后不能明显减少,那无疑就是浪费机器的I/O资源。