mapreduce的分区是指把map任务执行完后得到的数据根据partitioner的规则分配到不同的reducer上处理,(不同的reduce的输出结果会在不同的文件中)
一、什么时候分区?
每个Mapper执行完map任务后还会在当前节点对map输出数据进行partitioner和sort操作,做完这些操作之后把结果放到内存的缓冲区中。当存储大小超过缓冲区阈值,会把内存的缓冲区数据放到当前节点的本地磁盘的临时磁盘目录文件中存储(而且文件也不会很大,如果这个文件大小也超过指定大小,那么会分成多个磁盘文件)
(1)分区的实现
通过继承Partitioner类,实现getPartition方法。
public int getPartition(KEY key, VALUE value, int reduceTaskNumber)
map结束后得到的每个key-value都调用该方法,并把key与value都传进去。
第三个参数是当前job的reduce任务数。通过job.setNumReduceTasks(7)指定,这里设为7,则表示job会在yarn集群中启动7个reducer。
7个reducer,每个reducer都会有编号,从0~6,和数组索引一样。getPartition方法的返回值就是返回reducer的编号,表示当前keyu-value会被分配到哪个reducer上进行reduce处理。
比如:
//分区相关
job.setPartitionerClass(FlowAreaPartitioner.class);
job.setNumReduceTasks(7);
private final HashMap<String, Integer> areaMap = new HashMap<>();
{
areaMap.put("135", 0);
areaMap.put("185", 1);
areaMap.put("156", 2);
areaMap.put("171", 3);
areaMap.put("131", 4);
areaMap.put("123", 5);
}
@Override
public int getPartition(Text key, FlowDTO value, int reduceTaskNumber) {
Integer provinceCode = areaMap.get(key.toString().substring(0, 3));
//如果出现手机号不在字典中的,则把这些手机号分配到单独一个reduce上处理(编号为6的reduce,第七个)
return provinceCode == null ? reduceTaskNumber - 1 : provinceCode;
}
以上面代码为例,key为手机号,value为该手机号所使用的流量信息。代码会根据手机号的前缀判断,把当前key-value分配到不同的reducer上。
注意:
<1>当reducer任务数为1时,即job.setNumReduceTasks(1);或者没有进行设置时,不论getPartition方法返回什么值,都只分配到同一个reducer上,结果都输出到同一个文件。
<2>除了<1>的情况外,getPartition方法返回值必须小于reducer任务数量。也就是说,当job.setNumReduceTasks(7)时,getPartition返回值的范围只能是0~6,如果超出该范围则抛异常。