MapReduce学习笔记和总结(三)— mapTask & reduceTask

目录

MapReduce运行时的mapTask和reduceTask

1 mapTask任务

1.1 mapTask & mapTask并行度

1.2 如何修改mapTask并行度

2 reduceTask任务

2.1 reduceTask & reduceTask并行度

2.2 如何设置reduceTask的并行度

2.3 数据倾斜问题


MapReduce运行时的mapTask和reduceTask

1 mapTask任务

1.1 mapTask & mapTask并行度

mapTask任务的并行度:分布式运行mapTask任务的个数叫做并行度。

mapTask任务实际上就是分而治之中,分了多少个小任务,类似WordCount程序中map函数的调用次数。而最终mapTask的任务划分与HDFS底层存储的数据块有直接关系。

Hadoop源码中的FileInputFormat中的getSplits方法,决定每个mapTask的任务划分。该方法中有个split(切片)的概念。

切片(split)也称为逻辑切片,即只是对数据进行了逻辑划分,并没有真正的进行数据切分。例如:数据300M,mapTask01:0-100M、mapTask02:100-200M、mapTask03:200-300M。

这个逻辑切片,默认的大小是一个数据块的大小(BlockSize),那为什么是一个数据块的大小呢?

详细解释如下:

因为底层存储块的默认大小是128M,不同的数据块可能存储在不同的节点上,所以切片大小理论上128M合适,避免引切片过大、过小,而造成跨块计算,也避免了不必要的网络传输。

源码中的splitSize:

computeSplitSize方法就是求这三个参数的中间值大小,默认是blockSize大小(128M);

当自定义切片大小时,若想要切片大小小于BlockSize,改maxSize;若想要切片大小大于BlockSize,改minSize;

同时,getSplits方法实际上就是获取每一个逻辑切片。mapTask任务实际最终对应到数据上就是对应一个split切片。即一个切片,一个mapTask。也就是说:一个split切片 <----> 一个mapTask <----> 一个YarnChild

综上,任务划分就是对原始处理的数据进行任务切分(让不同的数据跑在不同的节点上),最终启动mapTask的个数与切片大小有关(切片默认大小是128M)

思考以下问题:

1)切片与数据块的关系?

答:没啥实际关系,切片是数据计算逻辑的划分,块是数据存储的物理划分。

2)MR统计3个300M的单词数据文件,需要启动几个mapTask任务?

答:9个

3)MR统计1个300M的单词数据文件,切片大小设置为200M,启动几个mapTask任务?

答:2个;mapTask-01:0-200M、mapTask-02:201-300M;但实际存储的是3个数据块。

4)MR统计8个300B的单词数据文件,切片大小设置为10M,启动几个mapTask?

答:8个;这种情况下切片的大小是实际处理的数据文件的大小。文件大小小于split大小,默认不会将文件进行合并。

在生产中,如果出现了大量小文件,可将原来默认的文件输入类FileInputFormat(TextInputFormat)修改为CombineTextInputFormat,该类可将文件进行合并,同时修改切片设置,可提高程序运行效率;

job.setInputFormatClass(CombineTextInputFormat.class);
CombineTextInputFormat.setMaxInputSplitSize(job,10*1024*1024);

这样,就会对文件进行合并读取,10M一个split(mapTask);即:合并文件后进行mapTask任务划分。

综上可知:块决定存储,切片决定mapTask!!!

1.2 如何修改mapTask并行度

想要修改mapTask的并行度,就需要修改split的大小,修改方式有如下两种:

(1)修改mapred-site.xml配置文件

 修改配置文件中:mapreduce.input.fileinputformat.split.maxsize和mapreduce.input.fileinputformat.split.minsize两个配置项;

一般不采用该方法,因为修改配置项后,对别的MR程序也会造成影响,属于一变万变。 

(2)修改代码

在FileInputFormat中使用以下方法,这种方式只对当前程序有效;

2 reduceTask任务

2.1 reduceTask & reduceTask并行度

reduceTask就是:reduce端进行任务分配的时候的每一个小任务;

reduceTask的并行度就是:reduceTask并行运行的个数;

reduceTask的并行度与分区个数有关

reduceTask的并行度在显示上,就是最终输出结果的个数;若最后数据结果是N个,那么就是启动了N个reduceTask;

当数据量特别大的时候,若只有一个reduceTask,即只在一个节点中进行结果合并;这会严重导致性能不高等问题。

2.2 如何设置reduceTask的并行度

可以通过修改驱动类代码,设置reduceTask个数,传N就是N个reduceTask,如:part-r-00000、part-r-00001、…、part-0000n,生成的N个文件是各自统计不同的key的结果。最后,n个文件合并到一起就是我们最终的统计结果。

job.setNumReduceTasks(n);

该方法的内部是由MR的默认分区方式,决定了由不同的map输出的key,最终进入到不同的reduceTask中。

分区在map之后,reduce之前,即:shuffle阶段。shuffle阶段详细介绍地址:

MR中的分区方式包括:默认分区方法自定义方法两种。下面对这两种分区方法别进行介绍:

1)默认分区方法

Partitioner(默认的分区类)<---->  HashPartitioner(默认实现的分区)

public int getPartition(K key, V value,int numReduceTasks) {
    return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
}

key:map端输出的key;

value:map端输出的value;

numPartitions:reduceTask的个数;是由job.setNumReduceTask()方法设置。

默认分区方法是根据map端输出的Key的HashCode()值,进行取余分区的。

最终,模的结果通过这个函数得到不同的余数,进入到不同的reduceTask中进行计算。同时也可以保证,相同的key进入相同的reduceTask中。

综上,reduceTask并行度 <----> job.setNumReduceTask() <----> 设置了几个就有几个reduceTask <----> 最终对应输出

2)自定义分区方法

工作中,我们会经常根据业务需求进行自定义分区,比如:统计各地区的手机流量,就需要按照各地区输出不同的统计结果。

思考该需求:进行单词统计时,将以a~j为首的单词存放到一个文件中,以k~z为首的单词的存放到另一个文件中。

具体实现自定义分区的流程为:

①创建自定义分区类,继承Partitioner类

②重写getPartition方法;返回的int值,就是part-r-0000x文件中x的值。

package com.ethan.mrpartition;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;

/**
 * 自定义分区规则
 * @author Ethan
 * Key:map输出key的类型
 * value:map输出value的类型
 *
 */
public class MyPartition extends Partitioner<Text, IntWritable>{
	/**
	 * Text key:map输出的key
	 * IntWritable value:map输出的value
	 * int numPartitions:分区个数 由job.setNumReduceTask()设置
	 */
	@Override
	public int getPartition(Text key, IntWritable value, int numPartitions) {
		String keyString = key.toString();
		char keyChar = keyString.charAt(0);
		if(keyChar >= 'a' && keyChar <= 'j') {
			return 0;
		}else {
			return 1;
		}
	}
}

类中的参数:

 Key:map输出key的类型;

value:map输出value的类型 

方法中的参数:

Text key:map输出的key;

IntWritable value:map输出的value;

int numPartitions:分区个数,通过job.setNumReduceTask()设置 

③设置分区个数

job.setNumReduceTasks(2);

④在驱动类中添加自定义分区类

job.setPartitionerClass(MyPartition.class);

总结:

最终决定输出文件个数的是setNumReduceTasks,而分区的作用仅仅是规划每个reduceTask应该计算的数据的范围。

(1)默认情况下:(key.hashCode & Integer.Max) % numReduceTasks---Key哈希取余。这是一种比较均匀的数据划分方式。

(2)自定义情况下:根据需求,按照自定义的规则规划reduceTask应该处理的数据。

本例中,reduceTask-01:a~j、reduceTask-02:k~z。

(3)特别的,当你自定义分区的个数(partition)大于设置的分区个数(reduceTask)的时候,程序会报非法分区错误;

只有设置分区为1时除外,因为设置分区为1时,所有结果输出到1个文件中。

当自定义分区个数,小于设置的分区个数是,程序能正常运行,只不过是生成的文件大于分区个数文件中没有内容而已。

没有内容是因为多生成了reduceTask,在那空跑。

(4)自定义分区的返回值的含义:与reduceTask的id相对应;正常情况下,reduceTask从0开始编号。

如果分区中返回0,代表对应reduceTask0,最终的结果文件对应part-r-00000

如果分区中返回2,代表对应reduceTask0,最终的结果文件对应part-r-00002

2.3 数据倾斜问题

reduceTask的并行度最终会影响MR程序的整体运行效率,在生产环境中,mapTask(切片)基本不用改。但reduceTask全凭自己设置。所以在设计分区的时候,一定要足够的了解数据,如果设计分区不理想,容易产生数据倾斜。

(1)什么是数据倾斜?

每个reduceTask处理的数据不均匀。其造成的直接后果是影响代码的整体执行效率,比如:10个reduceTask,9个已经完成,但有1个始终完成不了。这9个就要一直等待那一个完成,极大的拉低了执行效率。

(2)如何避免数据倾斜?

合理设计分区,只能减少,无法完全消除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值