1.什么是MapReduce
MapReduce是Google公司的核心计算模型,我在前面提到过,Google的三大论文。hadoop受到Google的启发开发出自己的MapReduce框架,基于这个框架写出的应用程序能够在上千台计算机上组成大型集群,并以一种可靠容错的方式并行处理上T级别的数据,实现hadoop在集群上的数据和任务并行计算与处理
1.一个MapReduce作业通常会把输入的数据集切分成若干个独立的数据块,由Map Task以完成并行的方式处理他们。对于Map的输出,框架会首先进行排序,然后把结果输入给Reduce Task。
2.通常计算节点和数据节点在一起,这样可以减少网络带宽中数据的传输,达到高效利用
MapReduce有两个角色:
1.JobTracker:负责任务的管理和调度(一个hadoop集群中只有一台JobTracker)
2.TaskTracker:负责执行工作,在DataNode节点上执行(Map函数和Reduce函数运行的节点)
下图是MapReduce的流程简介:
2.MapReduce处理流程
我们使用hadoop里面的一个简单的例子看一下MapReduce的处理流程。打开$HADOOPHOME\hadoop-1.0.4\src\examples\org\apache\hadoop\examples下面有一个WordCount.java。代码如下:
package org.apache.hadoop.examples;
import java.io.IOException;
import java.util.StringTokenizer;
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.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;
public class WordCount {
/**
*
* @title TokenizerMapper
* @description Mapper后面的四个参数含义:
* Object:输入到Map中的Key
* Text:输入到Map中的Value
* Text:Map的输出Key也是Reduce的输入的Key
* IntWritable:Map的输出Value也是Reduce的输入Value
* @author hadoop
* @version WordCount
* @copyright (c) SINOSOFT
*
*/
public static class TokenizerMapper
extends Mapper<Object, Text, Text, IntWritable>{
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
/**
* 重写Map函数
* Map函数的意思:使用空格切割value。生成一个一个的单词。
* 模拟Map的输入:<偏移量,"hadoop hadoop haha hive redis redis LVS LVS NB">
* 模拟Map的输出:<hadoop,1>;<hadoop,1>;<haha,1>;<hive,1>;<redis,1>;<redis,1>;<LVS,1>;<LVS,1>;<NB,1>
*/
public void map(Object key, Text value, Context context ) throws IOException, InterruptedException {
//String类的工具类,可以切割字符串。默认使用空格切割字符串
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
}
/**
*
* @title IntSumReducer
* @description Reduce函数中Reducer<Text,IntWritable,Text,IntWritable>的泛型含义
* Text:输入到Reduce中的Key,也是Map输出的Key
* IntWritable:输入到Reduce中的Value,也是Map输出的Value
* Text:Reduce的输出Key
* IntWritable:Reduce的输出Value
* @author hadoop
* @version WordCount
* @copyright (c) SINOSOFT
*
*/
public static class IntSumReducer extends Reducer<Text,IntWritable,Text,IntWritable> {
private IntWritable result = new IntWritable();
/**
* 在Reduce函数的输入是Map函数的输出,并且Map输出相同的Key会放在一起,也就生成了List类型的Value
* 模拟Reduce的输入前:<hadoop,1>;<hadoop,1>;<haha,1>;<hive,1>;<redis,1>;<redis,1>;<LVS,1>;<LVS,1>;<NB,1>
* Reduce中的洗牌:<hadoop,List values>;<haha,List values>;<redis,List values>;<LVS,List values>;<NB,List values>:相同的Key放在一组
* 模拟Reduce的输入:<hadoop,List values>;<haha,List values>;<redis,List values>;<LVS,List values>;<NB,List values>
* 模拟Reduce的输出:<hadoop, 2>;<haha, 1>;<redis, 2>;<LVS, 2>;<NB, 1>
*/
public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
context.write(key, result);
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = new Job(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(TokenizerMapper.class);
job.setCombinerClass(IntSumReducer.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job, new Path("/user/helloMR")); //Map的输入
FileOutputFormat.setOutputPath(job, new Path("/user/helloHDFS/success"));//Reduce的输出
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
在main方法中有:FileInputFormat.addInputPath(job, new Path("/user/helloMR"));
提供了Map阶段的输入,具体是怎么做到的呢?
在hadoop中使用InputSplit将输入的数据发送给每个单独的Map,InputSplit并非保存数据本身,而是一个分片长度和一个记录数据位置的数组,InputSplit对象可以通过Inputformat()来生成。
数据-->Map将分片-->InputFormat()-->getRecordReader()-->生成RecordReader-->通过createKey();createValue()-->Map需要的<key,Value>
现在可以在eclipse里面运行:
3.WordCount中的数据流程