MapReduce是Hadoop提供的分布式并行计算框架,用户不用关心如何编写实现分布式并行计算代码,只需在Mapper 和Reducer 里实现自己业务逻辑就可以了。简化了编写分布式程序的复杂度。
Hadoop中的mapreduce计算模型也是基于分布式计算原型的,是分布式计算的一种实现。Hadoop提供了mapreduce框架的底层实现,负责完成mapreduce程序分发到各个nodeManager节点上,map计算后的结果如何分发到对应的reduce汇总节点,数据的分发,任务的启动,监控和资源的调度等等。开发人员只需要按照一定规则和约定开发map 和reduce的具体业务实现代码就可以,剩下的交给hadoop完成计算。
案例:计算单词出现的词频(本地运行模式)
文件E:\\word.txt,内容:
郑州,开封,洛阳,南阳,信阳,驻马店,安阳,
郑州,开封,洛阳,南阳,信阳,驻马店,安阳,
郑州,开封,洛阳,南阳,信阳,驻马店,安阳,
郑州,开封,洛阳,南阳,信阳,驻马店,安阳,
郑州,开封,洛阳,南阳,信阳,驻马店,安阳,
郑州,开封,洛阳,南阳,信阳,驻马店,安阳,
周口,周口,郑州,开封,洛阳,周口,开封
1.mapper类
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;public class WordCountMapper extends Mapper<LongWritable, Text, Text, LongWritable> {
@Override
protected void map(LongWritable key, Text line, Mapper<LongWritable, Text, Text, LongWritable>.Context context)
throws IOException, InterruptedException {
// 需求:文本中单词词频统计
// 通过mapper task 处理每一行的数据,并且写入到上下文中
// 行格式: 郑州,开封,洛阳,新乡.....
String[] words = line.toString().split(",");
System.out.println(words.length);
for (String w : words) {
System.out.println(w);
context.write(new Text(w), new LongWritable(1));
}
}}
2.reducer类
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;public class WordCountReducer extends Reducer<Text, LongWritable, Text, LongWritable> {
@Override
protected void reduce(Text text, Iterable<LongWritable> counts,
Reducer<Text, LongWritable, Text, LongWritable>.Context context) throws IOException, InterruptedException {
long sum = 0;
for (LongWritable i : counts) {
System.out.println(i);
sum += 1;
}
context.write(text, new LongWritable(sum));
}
}
3.主函数入口类
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
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;public class WordCountDriverMain {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "WordCountDriverMain");
// 指定job要用到的mapper/reducer业务类
job.setJarByClass(WordCountDriverMain.class);
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);// 指定mapper输出数据用的kv类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);// 指定最终输出结果的kv类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);// 指定job输入的源文件所在目录
FileInputFormat.setInputPaths(job, new Path("E:\\word.txt"));
// 指定job输出的文件目录
FileOutputFormat.setOutputPath(job, new Path("E:\\output"));boolean result = job.waitForCompletion(true);
if (result) {
System.out.println("success");
} else {
System.err.println("failed");
}
}
}
输出结果:E:\\output\\part-r-00000
信阳 6
南阳 6
周口 3
安阳 6
开封 8
洛阳 7
郑州 7
驻马店 6
MapReduce工作原理流程简介
在MapReduce整个过程可以概括为以下过程:
输入 --> map --> shuffle --> reduce -->输出
输入文件会被切分成多个块,每一块都有一个map task
map阶段的输出结果会先写到内存缓冲区,然后由缓冲区写到磁盘上。默认的缓冲区大小是100M,溢出的百分比是0.8,也就是说当缓冲区中达到80M的时候就会往磁盘上写。如果map计算完成后的中间结果没有达到80M,最终也是要写到磁盘上的,因为它最终还是要形成文件。那么,在往磁盘上写的时候会进行分区和排序。一个map的输出可能有多个这个的文件,这些文件最终会合并成一个,这就是这个map的输出文件。
流程说明如下:
1、输入文件分片,每一片都由一个MapTask来处理
2、Map输出的中间结果会先放在内存缓冲区中,这个缓冲区的大小默认是100M,当缓冲区中的内容达到80%时(80M)会将缓冲区的内容写到磁盘上。也就是说,一个map会输出一个或者多个这样的文件,如果一个map输出的全部内容没有超过限制,那么最终也会发生这个写磁盘的操作,只不过是写几次的问题。
3、从缓冲区写到磁盘的时候,会进行分区并排序,分区指的是某个key应该进入到哪个分区,同一分区中的key会进行排序,如果定义了Combiner的话,也会进行combine操作
4、如果一个map产生的中间结果存放到多个文件,那么这些文件最终会合并成一个文件,这个合并过程不会改变分区数量,只会减少文件数量。例如,假设分了3个区,4个文件,那么最终会合并成1个文件,3个区
5、以上只是一个map的输出,接下来进入reduce阶段
6、每个reducer对应一个ReduceTask,在真正开始reduce之前,先要从分区中抓取数据
7、相同的分区的数据会进入同一个reduce。这一步中会从所有map输出中抓取某一分区的数据,在抓取的过程中伴随着排序、合并。
8、reduce输出