MapReduce中各个阶段的分析:

在MapReduce的各个阶段:
在文件被读入的时候调用的是Inputformat方法读入的。inputformat——>recordreader——>read(一行) 。在这里读入一行,返回的是(k,v)的形式,key是行号的偏移量,value的值是这一行的内容。
在上述的过程中,之后是调用map方法,将以上内容转换成正真的(key,value)的形式。key为值,value为1,然后调用context.write方法将该数据写出来。
从map端写出来之后具体写到outputcollector收集器中。
经过outputcollector收集器之后会写入到环形缓缓区中。在环形缓冲区中会做几件事情,
①排序,调用的是快速排序法。
②分区,调用的是hashpartitioner分区。达到80%之后会溢写磁盘。分区中hashpartitioner分区的时候是按照key进行hash取值的。相同的hash值会在一个分区中,取几个分区可以人为设定。排序的时候的两个依据是partition和key两个作为依据的。同一个partition中是按照key进行排序的。
环形缓冲区中的数据会spill溢写到磁盘中。
在溢写到磁盘之后会merge,归并排序,将多个小文件merge成大文件的。所以合并之后的大文件还是分区,并且分区内部是有序的。
在这里map阶段就算结束了,后边就是reduce阶段了,reduce阶段会去map阶段merge之后的文件中拿数据,按照相同的分区去取数据。reduce中是有分区号的,将数据拿过来之后会存储在本地磁盘中。
取完数据之后会按照相同的分区,再将取过来的数据进行merge归并排序,大文件的内容按照key有序进行排序。
之后会调用groupingcomparator进行分组,之后的reduce中会按照这个分组,每次取出一组数据,调用reduce中自定义的方法进行处理。
最后调用outputformat会将内容写入到文件中。

在这里map端输入的(key,value)的类型我们人为可以指定,一般会设置为(LongWritable,Text),为什么会是longwritable呢,因为map端正真进来的时候是切分之后的文件。key的值是读取的行的偏移量。
例子:

* Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
* 它的这个Mapper让你去定义四个泛型,为什么mapper里面需要四个泛型
* 其实读文本文件的操作不用你来实现,框架已经帮你实现了,框架可以读这个文件
* 然后每读一行,就会发给你这个map,让你去运行一次,所以它读一行是不是把数据传给你,
*
* 那他传给map的时候,这个数据就意味着类型的一个协议,我以什么类型的数据给你,我是不是得事先定好啊
* map接收的数据类型得和框架给他的数据类型一致,不然的话就会出现类型转换异常
* 所以map里面得定数据类型,前面两个是map拿数据的类型,拿数据是以什么类型拿的,那么框架就是以这个类型传给你
*
* 另外两个泛型是map的输出数据类型,即reduce也得有4个泛型,前面两个是reduce拿数据的泛型得和map输出的泛型类型一致
* 剩下两个是reduce再输出的结果时的两个数据类型
*/
/*
* 4个泛型,前两个是指定mapper端输入数据的类型,为什么呢,mapper和reducer都一样
* 拿数据,输出数据都是以<key,value>的形式进行的--那么key,value都分别有一个数据类型
* KEYIN:输入的key的类型
* VALUEIN:输入的value的类型
* KEYOUT:输出的key的数据类型
* VALUEOUT:输出的value的数据累心
* map reduce的数据输入输出都是以key,value对封装的
* 至于输入的key,value形式我们是不能控制的,是框架传给我们的,
* 框架传给我们是什么类型,我们这里就写什么数据类型
* 默认情况下框架传给我们的mapper的输入数据中,key是要处理的文本中一行的起始偏移量,
* 因为我们的框架是读一行就调用一次我们的偏移量
* 那么就把一行的起始偏移量作为key,这一行的内容作为value
* 那么输出端的数据类型是什么,由于我们输出的数<hello,1>
* 那么它们的数据类型就显而易见了
* 初步定义为:
* Mapper<Long, String, String, int>
* 但是不管是Long还是String,在MapReduce里面运行的时候,这个数据读到网络里面进行传递
* 即各个节点之间会进行传递,那么要在网络里面传输,那么就意味着这个数据得序列化
* Long、String对象,内存对象走网络都得序列化,Long、String,int序列化
* 如果自己实现Serializable接口,那么附加的信息太多了
* hadoop实现了自己的一套序列化机制
* 所以就不要用Java里面的数据类型了,而是用它自己的封装一套数据类型
* 这样就有助于提高效率,实现了自己的序列化接口
* 在序列化传输的 时候走的就是自己的序列化方法来传递,少了很多负载信息,传递数据精简,
* Long---LongWritable
* String也有自己的封装-Text
* int--IntWritable
WCMapper.java
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
/**
* Description: Mapper<br/>
* Copyright (c) , 2018, xlj <br/>
* This program is protected by copyright laws. <br/>
* Program Name:WCMapper.java <br/>
*
* @version : 1.0
*
*/
public class WCMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
// MapReduce框架每读一次数据,就会调用一次该方法
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
//具体业务逻辑就写在这个方法体中,而且我们业务要处理的数据已经被框架传递进来,在方法参数中
//key--这一行数据的其实偏移量 value--这一行数据的文本内容
//1.先把单词拿出来,拿到一行
String line = value.toString();
//2.切分单词,这个是按照特定的分隔符 进行切分
String [] words = line.split(" ");
//3.把里面的单词发送出去
/*
* 怎么发出去呢?我都不知道reduce在哪里运行
* 其实呢,这个不用我们关心
* 你只要把你的东西给那个工具就可以了
* 剩下的就给那个框架去做
* 那个工具在哪-----context
* 它把那个工具放到那个context里面去了,即输出的工具
* 所以你只要输出到context里面就行了
* 剩下的具体往哪里走,是context的事情
*/
//遍历单词数组,输出为<K,V>形式 key是单词,value是1
for (String word : words) {
//记得把key和value继续封装起来,即下面
context.write(new Text(word), new IntWritable(1));
}
/*
* map方法的执行频率:每读一行就调一次
* 最后到reduce 的时候,应该是把某个单词里面所有的1都到,才能处理
* 而且中间有一个缓存的过程,因为每个map的处理速度都不会完全一致
* 等那个单词所有的1都到齐了才传给reduce
*/
//每一组key,value都全了,才会去调用一次reduce,reduce直接去处理valuelist
//接着就是写Reduce逻辑了
}
}
WCReducer.java
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
/**
* Description: Reducer<br/>
* Copyright (c) , 2018, xlj <br/>
* This program is protected by copyright laws. <br/>
* Program Name:WCReducer.java <br/>
*
* @version : 1.0
*/
/*
* 类型记得要对应
*/
public class WCReducer extends Reducer<Text, IntWritable, Text, Text> {
//map处理之后,value传过来的是一个value的集合
//框架在map处理完成之后,将所有的KV对保存起来,进行分组,然后传递一个组,调用一次reduce
//相同的key在一个组
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException {
//遍历valuelist,进行了累加
int count = 0;
for (IntWritable value : values) {
//get()方法就能拿到里面的值
count += value.get();
}
//输出一组(一个单词)的统计结果
//默认输出到HDFS的一个文件上面去,放在HDFS的某个目录下
context.write(key, new Text(count+""));
//但是还差一个描述类:用来描述整个逻辑
/*
* Map,Reducce都是个分散的,那集群运行的时候不知道运行哪些MapReduce
*
* 处理业务逻辑的一个整体,叫做job
* 我们就可以把那个job告诉那个集群,我们此次运行的是哪个job,
* job里面用的哪个作为Mapper,哪个业务作为Reducer,我们得指定
*
* 所以还得写一个类用来描述处理业务逻辑
* 把一个特定的业务处理逻辑叫做一个job(作业),我们就可以把这个job告诉那个集群,
*
*/
}
}
WCRunner.java
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
/**
* Description: 用来描述一个特定的作业<br/>
* Copyright (c) , 2018, xlj <br/>
* This program is protected by copyright laws. <br/>
* Program Name:WCRunner.java <br/>
*
* 该作业使用哪个类作为逻辑处理的map
* 哪个作为reduce
* 还可以指定该作业要处理的数据所在的路径
* 还可以指定该作业输出的结果放到哪个路径
*
* @version : 1.0
*/
public class WCRunner {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//首先要描述一个作业,这些信息是挺多的,哪个是map,哪个是reduce,输入输出路径在哪
//一般来说这么多信息,就可以把它封装在一个对象里面,那么这个对象呢就是 ----Job对象
Job job = Job.getInstance(new Configuration());
//job用哪个类作为Mapper 指定输入输出数据类型是什么
job.setMapperClass(WCMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//job用哪个类作为Reducer 指定数据输入输出类型是什么
job.setReducerClass(WCReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
//指定原始数据存放在哪里
//参数1:里面是对哪个参数进行指定
//参数2:文件在哪个路径下,这个路径下的所有文件都会去读的
FileInputFormat.setInputPaths(job, new Path("input/data1"));
//指定处理结果的数据存放路径
FileOutputFormat.setOutputPath(job, new Path("output1"));
//提交
int isok = job.waitForCompletion(true)?0:-1;
System.exit(isok);
}
}

本文深入解析MapReduce的工作流程,从输入格式的读取到Map任务的执行,再到数据的溢写、排序、分区,直至Reduce阶段的数据处理和最终输出。通过实例说明MapReduce如何处理大规模数据集。
&spm=1001.2101.3001.5002&articleId=100979094&d=1&t=3&u=7c8ffda2ea844f2ba17f983644265d0b)
274

被折叠的 条评论
为什么被折叠?



