MR核心编程思想总结
一、MR思想初识
先分后合思想,MR程序分为map和ruduce两个阶段,map阶段负责将海量数据分类形成kv键值对,reduce阶段负责将map阶段形成的kv键值对分组合并。
1、首先待计算的数据在client端生成切片(逻辑上对数据进行划分) , 生成的切片个数对应着要启动多少个MapTask程序进行Map阶段的计算.
2、多个MapTask程序是并行运行的,互不相干.
3、在每个MapTask中对数据的处理要考虑到很多细节, 是否有分区, 如何排序, 数据如何写磁盘等..
4、多个MapTask计算完成后,每个MapTask都会有输出的数据
5、会根据分区的个数决定启动多少个ReduceTask(逻辑上来说), 实际上是 启动多少个ReduceTask就会生成多少个分区.
6、每个ReduceTask会到每个MapTask中拷贝自己所要处理的数据,说白了就是对应的分区的数据.
7、每个ReduceTask最终也会输出最后的结果.
代码案例详见:手写MR程序之wordcount案例https://blog.csdn.net/weixin_42796403/article/details/109749080
MR核心思想详见:MapReduce核心思想https://blog.csdn.net/weixin_42796403/article/details/109732859
二、MR程序组成
一个完整的MR程序在分布式运行时有三类实例进程运行
1、MrAppMaster:负责整个程序运行过程的调度和状态协调
–MrAppMaster可以理解为是ApplicationMaster的具体实现类,每运行一个mr程序,就会生成一个 MrAppMaster对象,负责监控MR程序的执行过程
2、MapTask:负责Map阶段的整个数据处理流程
3、ReduceTask:负责Reduce阶段的整个数据处理流程
通过官方wordcount案例分析:
编写一个可运行的MR程序,需要3部分:
1、自定义mapper(需继承Mapper父类,重写map()方法)
public class Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {} --父类
四个泛型: 两组kv对
输入数据的kv类型:
–KEYIN : LongWritable 表示文件读取数据的偏移量,简单理解为每次从文件的哪个位置开始读取数据.
–VALUEIN : Text 实际从文件中读取的一行数据
输出数据的kv类型:
–KEYOUT : Text 表示一个单词
–VALUEOUT : IntWritable 表示这个单词出现了1次.
2、自定义reducer(需继承Reducer父类,重写reduce()方法)
public class Reducer<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {} --父类
四个泛型: 两组kv对
输入数据的kv类型: Reducer输入数据的kv类型要对应Mapper输出数据的kv类型
–KEYIN : Text 表示一个单词
–VALUEIN : IntWritable 表示该单词出现了1次
输出数据的kv类型:
–KEYOUT : Text 表示一个单词
–VALUEOUT: IntWritable 表示该单词总共出现的次数
3、驱动类Driver
在Driver类中需指定MR运行的环境、Map和Reduce运行类、输入输出类型、输入输出路径、分区、是否聚集等信息。
将我们写好的Mapper和Reducer打包成一个Job,可以提交到本地或者是Yarn上执行
三、MR程序内部方法剖析
/*
Mapper类
1、 所有的MapReduce程序中的自定义Mapper类都需要继承Mapper类.
2、 setup(): 在MapTask开始执行时调用1次.
3、 map() : 为输入数据的每个kv都调用一次map方法.大部分的MapReduce程序都需要重写该方法.
4、 cleanup(): 在MapTask结束前调用1次
5、 run()方法: 控制Mapper中的 setup map cleanup执行的方法
*/
public void run(Context context) throws IOException, InterruptedException {
setup(context); // setup方法执行了一次
try {
while (context.nextKeyValue()) { //判断是否还有下一个输入的kv
// 执行map方法,传入当前key 当前value
map(context.getCurrentKey(), context.getCurrentValue(), context);
}
} finally {
cleanup(context); // cleanup方法执行了一次
}
}
/*
Reducer类
1、 所有的MapReduce程序中的自定义Reducer类都需要继承Reducer类.
2、 setup():在ReduceTask开始执行时调用1次
3、 reduce(): 为每个key调用1次reduce方法,大部分的MapReduce程序都需要重写该方法.
map写出的N个kv,其中会有很多相同的k, 在进入到reduce方法前,会按照k进行分组,把相同k的多个kv对 分成一个组中. 一组的数据会执行一次reduce方法.
4、 cleanup(): 在ReduceTask结束前调用1次
5、 run(): 控制Reducer中的 setup reduce cleanup执行的方法
*/
public void run(Context context) throws IOException, InterruptedException {
setup(context); //调用1次setup方法
try {
while (context.nextKey()) { //是否有下个key
//执行reduce方法,输入当前key和values
reduce(context.getCurrentKey(), context.getValues(), context);
// If a back up store is used, reset it
Iterator<VALUEIN> iter = context.getValues().iterator();
if(iter instanceof ReduceContext.ValueIterator) {
((ReduceContext.ValueIterator<VALUEIN>)iter).resetBackupStore();
}
}
} finally {
cleanup(context); //调用1次cleanup方法
}
}