对于大数据搬砖工来说,使用MapReduce调优是家常便饭,其中就必须知晓mapper和reducer个数的决定原理,参考了很多有关mapper个数的文章,大部分都非常零散,只讲其中一两点,下面是自己拼凑总结,并结合实际经验梳理的,供参考~
目录
2)具体CombineHiveInputFormat类切片算法逻辑
1)长尾任务:某几个mapper的latency超过平均mapper的latency的2倍
情况二:MR输出的小文件,如reduce过程中产生的小文件,通过日志可以看到
3.2 数据量较大,分配到每个mapper任务处理的数据量也很大,单个mapper任务执行时间长
注:
1)对mapper的split切片:逻辑切片;对block的切片:物理切片
2)为什么默认map的split大小是hdfs中的块大小128MB呢?
在集群中,数据被分割成多个block存在hdfs中不同的datanode上,而当一个MR任务对数据处理时,先要从datanode上获取所需数据块,而当这个MR任务的split大小刚好等于文件块大小时,直接访问本地的datanode节点,来扫描hdfs文件块,减少跨节点的文件扫描,减少网络IO
即当逻辑切片大小 = 物理切片大小,一般情况下最优
3)并行度 = 个数
4)实际 mapper个数调优需多次验证决定
- mapper个数太多:太多的资源读取、磁盘io、网络Io、每个task创建和退出费时
- mapper个数太少:不能充分利用资源,每个mapper处理的数据量过大,费时
例子:两张表join
- 大表总共158749566行,文件总大小4.4G,存储个数22个文件,每个大小200Mb左右。
- 小表总共1979375 行,文件大小50.7Mb,存储个数2个文件,大小50Mb以内。
1 因素一:文件压缩算法是否支持文件分片
- 在map阶段,会先进入InputFormat判断:
- InputFormat类,用于对MapReduce作业输入的处理,InputFormat涉及3个接口/类,即InputFormat、InputSplit和RecordReader。
- 分别用来获取文件的逻辑切片,按照切片指定输入到一个个map;获取分片大小、将数据转化为kv值
- 其中下列的文件压缩算法不支持文件切片,此时一个map任务处理的数据量会>=每个文件的大小
例子:当压缩方式为deflate时:
使用不同参数执行上面代码产生的map数---------------------------------
--1.使用系统配置的默认值
set mapred.max.split.size = 256000000; --
set mapred.min.split.size = 256000000;
Hadoop job information for Stage-1: number of mappers: 24; number of reducers: 17--2.降低系统默认值
set mapred.max.split.size=134217728;
set mapred.min.split.size=134217728;
Hadoop job information for Stage-1: number of mappers: 24; number of reducers: 17--3.调高系统默认值
set mapred.max.split.size=500000000;
set mapred.min.split.size=256000000;
Hadoop job information for Stage-1: number of mappers: 9; number of reducers: 17--4.调高系统默认值
set mapred.max.split.size=1024000000;
set mapred.min.split.size=1024000000;
Hadoop job information for Stage-1: number of mappers:6 ; number of reducers: 17
- 发现:当降低splitsize参数值,本应增加map的个数,但是实际的map数量没变,而调高splitsize参数值时,本应减少map个数,实际上也减少了。
- 因为此时不支持对文件切分(即逻辑切分),map处理的数据量必须大于等于文件的大小,所以调低参数<文件大小时,也没用,而如果增大splitsize,相当于增大了一个map处理的数据量。
- 因为deflate压缩算法虽然不支持文件切分,但是可以进行文件合并。而从hive0.5开始就默认map前进行小文件合并了(默认的InputFormat类是CombineHiveInputFormat)
2 因素二:选择的InputFormat类
- HiveInputFormat类和CombineHiveInputFormat类都是继承自InputFormat类,但是从hive0.5开始默认是CombineHiveInputFormat类
- 不同的类,决定mapper个数的参数不一样,但都要从两方面来考虑:参数、参数优先级
2.1 HiveInputFormat类
注:
- 使用 HiveInputFormat类的企业已经很少
- 优先用方案一,方案二是方案一的简化。
方案一:确定map个数:参考:书《Hive性能调优实战》
首先取:max{defaultNum,mapred.map.tasks} 作为后续比较的备选
- 理解:
- 对文件的分块:物理切片;对input的数据切片:逻辑切片;defaultNum是当物理切片大小 = 逻辑切片大小时的map数量,此时两者完全匹配,减少了网络io和磁盘io,一般情况下,性能最优
- 只有当用户指定的期望的Map大小大于defaultNum时,才会发挥出所设置的map个数参数的效果,若是小于defaultNum,则每个mapper处理的数据量都不饱和,资源浪费,都不能达到一般情况下的性能最优,所设置的参数不起作用
- defaultNum:默认情况下Map的个数defaultNum=目标文件或数据的总大小totalSize/hdfs集群文件块的大小blockSize
其次分片大小取:max{mapred.min.split.size, blockSize} ,则个数取min{totalsize/mapred.min.split.size,totalsize/blockSize}
- 理解:当设置的mapred.min.split.size参数小于blockSize时,参数不起作用,因为一般的切片大小就为blockSize,当大于blockSize时这个参数才会有效
最后取min{max{defaultNum,mapred.map.tasks} , min{totalsize/mapred.min.split.size,totalsize/blockSize}}
即上面两个的较小值,即满足各种参数设置下的最小map数
方案二:确定splitsize来确定map数
参考:CSDN上的大多文章 :
真正让你明白Hive参数调优系列1:控制map个数与性能调优参数 (taodudu.cc)
hive如何调整map数和reduce数 - 简书 (jianshu.com)
按照上面的理解:
首先取:min{goalSize,blockSize}
- goalSize:totalSize/mapred.map.tasks
- 其实就相当于方案一的:max{defaultNum,mapred.map.tasks}
最后取:splitSize = max{mapred.min.split.size,min{goalSize,blockSize}}
- 相比于方案一,少了mapred.min.split.size和blocksize的比较,因为这个时候一般map的splitsize小于等于blocksize(128Mb)
总结:按照HiveInputFormat类,mapper数量=min{max{defaultNum,mapred.map.tasks} , min{totalsize/mapred.min.split.size,totalsize/blockSize}} 则决定mapper数量的外来参数只有mapred.map.tasks与mapred.min.split.size,当需要增加mapper数时,变大mapred.map.tasks或者变小mapred.min.split.size
2.2 CombineHiveInputFormat类
区别:CombineHiveInputFormat类相较 HiveInputFormat类自动实现对hdfs小文件的合并
map个数的计算:
1)由以下四个参数影响
- mapred.min.split.size 或者 mapreduce.input.fileinputformat.split.minsize(Hadoop2.0版本后这个参数的名字)
- mapred.max.split.size或者mapreduce.input.fileinputformat.split.maxsize
- mapred.min.split.size.per.rack
- mapred.min.split.size.per.node
还要与配置文件中的集群中可用mapper数量比较
2)具体CombineHiveInputFormat类切片算法逻辑
以下参考:(35条消息) hive执行时map任务数的确定_hive执行的job数是怎么确定的_大数据男的博客-CSDN博客
假设集群有3个机架,一共6个节点,9个block,假设每个block的大小都是100M。
设置合并后的最大分片为244M
set mapreduce.input.fileinputformat.split.maxsize=256000000;
设置一个节点上最小的分片大小,这个值设的不一样会影响分片数量。
set mapred.min.split.size.per.node=256000000;
设置 一个机架上最小的split大小,这个值设的不一样会影响分片数量。
set mapred.min.split.size.per.rack=256000000;
算法过程:
简单记:先从小范围(node)的小文件(文件大小<mapred.min.split.size.per.node)开始合并,如果还有剩余的,就在这几个节点(rack)的大范围再次合并小文件,如果还有剩余,在几个rack间再次合并。
- 节点内部
先从节点内部开始循环,机架1上node1三个分片之和为300M > 244M(设置的mapred.min.split.size.per.node一个节点最小的切片大小为244m),即A、B、C会合并为一个分片;
node2上只有一个block,大小是100M,小于节点最小分片大小,会暂时保留;node3和node2一样;
node4上有两个block,不够组成一个大分片;
- 同一个机架的节点间
从node2的block和node4的两个block,即G/H/F会拼成一个大分片;
机架1上node3上的block F会暂时保留;
- 机架间的合并
机架1上的F和机架2的I以及机架3上的H会组成一个大的分片;
3)具体的参数优先级(从小到大)
max.split.size <= min.split.size <= min.size.per.rack <= min.size.per.node
当四个参数设置矛盾时,系统会自动以优先级最高的参数为准,进行计算,如上面的算法说明中,mapreduce.input.fileinputformat.split.maxsize=256000000设置和 min.size.per.node一样,但是在实际中还是会将300M的合为一个分片。
注:
- 四个参数的配置大小一般满足:
max.split.size >= min.split.size >= min.size.per.rack >= min.size.per.node
- 在上面这个例子中mapper的个数与参数mapred.map.tasks基本无关,取上面四个参数得出的mapper数与集群中可用mapper数的较小值。
3 需要调整mapper个数的场景:
3.1 每一个mapper处理的数据量不均匀
1)长尾任务:某几个mapper的latency超过平均mapper的latency的2倍
- 解决:
- mapred.min.split.size
- mapred.max.split.size
2)大量小文件
(使用默认参数的,及默认输入格式FileInputFormat的MR会为每一个小文件都生成一个切片,浪费计算资源)
-
情况一:来自数据源输入的小文件
- 解决:使用CombineHiveInputFormat类,来合并一个DataNode上的小文件
- mapred.max.split.size限制合并文件数
- mapred.min.split.size.per.node决定这个node上的文件是否需要合并
- mapred.min.split.size.per.rack决定这个rack上的文件是否需要合并
- 解决:使用CombineHiveInputFormat类,来合并一个DataNode上的小文件
-
情况二:MR输出的小文件,如reduce过程中产生的小文件,通过日志可以看到
- 解决:
- set hive.merge.mapfiles=true; 默认值ture,在Map-only的任务结束时合并小文件
- set hive.merge.mapredfiles=true; 默认值false,在Map-Reduce的任务结束时合并小文件
- set hive.merge.size.per.task=256000000; 默认值256M((把处理小文件的几个mapper合成一个mapper)
- set hive.merge.smallfiles.avgsize=16000000
- 解决:
(默认值16M,当输出文件的平均大小小于16M时,启动一个独立的map-reduce任务进行文件merge)
3.2 数据量较大,分配到每个mapper任务处理的数据量也很大,单个mapper任务执行时间长
- 解决:
- 调整mapper数,是增加还是减少验证来看哪个快,有时太多的mapper,会增加启动和退出及落盘的时间
- splitSize = max{mapred.min.split.size,min{goalSize,blockSize}}