在Hadoop中,MapReduce起着非常重要的数据处理作用。HDFS作为数据存储的核心部件起着管理文件资源的作用,而MapReduce作为一种编程计算模型,对数据的分析和处理任务进行调度管理。
在MapReduce中,所有的机器分成两个角色,一个叫JobTracker,另外一个叫TaskTracker。同HDFS的分布式概念一样,JobTracker是用于执行总体调度的,一般运行在NameNode机器上,而TaskTracker则负责在节点DataNode上执行具体的分布式任务的计算。海量的数据的计算任务就被许多个TaskTracker将计算量分布在每一个节点上,从而最大程度的利用资源。
在MapReduce的入门也有一个HelloWord的例子,源代码如下:
package com.ant.ren.hadoop.demo; import java.io.IOException; import java.util.StringTokenizer; import org.apache.hadoop.conf.Configured; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.LongWritable; 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.util.Tool; import org.apache.hadoop.util.ToolRunner; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.input.TextInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat; public class WordCount extends Configured implements Tool{ /** * you can create any class extends Mapper stand for map * in map stage, we just split input file content by StringTokenizer default token: whitespace * then use word as key, show up count (1) as value **/ public static class Map extends Mapper<LongWritable, Text, Text, IntWritable>{ private final static IntWritable one = new IntWritable(1); private Text word = new Text(); public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException{ String line = value.toString(); StringTokenizer tokenizer = new StringTokenizer(line); while(tokenizer.hasMoreElements()){ word.set(tokenizer.nextToken()); context.write(word, one); } } } /** * you can create any class extends Reducer stand for reduce * in reduce stage, we iterate value for the same key (word), calculate the sum of their values * then output <word, sum> as reduce result **/ public static class Reduce extends Reducer<Text, IntWritable, Text, IntWritable>{ public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException{ int sum = 0; for(IntWritable val : values) sum += val.get(); context.write(key, new IntWritable(sum)); } } @Override public int run(String[] args) throws Exception { Job job = new Job(getConf()); job.setJarByClass(WordCount.class); job.setJobName("Word Count Job"); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); job.setMapperClass(Map.class); job.setReducerClass(Reduce.class); job.setInputFormatClass(TextInputFormat.class); job.setOutputFormatClass(TextOutputFormat.class); FileInputFormat.setInputPaths(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); boolean success = job.waitForCompletion(true); return success ? 0 : 1; } public static void main(String[] args) throws Exception{ int ret = ToolRunner.run(new WordCount(), args); System.exit(ret); } }
它演示的是对于输入的两个文件,统计它们内容里每一个单词出现的次数,并输出一张统计结果。例如给定这样两个文件的输入:
"file-1" hello world hello everyone "file-2" hello hadoop goodbye hadoop
那么输出的统计结果将会是这个样子:
hello 4 world 1 everyone 1 hadoop 2
在Hadoop中,每个MapReduce任务都被初始化为一个Job。每个Job又可以分为两个阶段:map阶段和reduce阶段。在map阶段,由map函数会接受一个如<key,value>形式的输入,然后产生一个<key, value>形式的中间输出。Hadoop会将所有具有相同key值的<key, value>输入集合到一起,传递给第二阶段的reduce函数,所以reduce函数看到的将不是<key, value>,而是<key, list of values>。第二阶段的reduce函数会对这个每个<key, list of values>进行处理,然后选择产生0个或1个最终的<key, value>输出。之所有选择类似map输出的<key, value>形式作为reduce输出也是考虑到这样的输出不需要处理就可以直接作为下一个map的输入,这样的模型最终可以方便对海量数据进行多重处理和分析。(易于实现和扩展)
在运行这个例子的时候,其实没必要把输入文件上传到Hadoop的HDFS里面去,如果只是为了根据自己需求调试和设计MapReduce的模型,只要机器上启动了MapReduce,没启动HDFS都可以。输入的话MapReduce程序会讲当前工作目录当做起点来搜索输入的数据。举例来说,就运行这个WordCount例子,就在当前工作目录下建一个目录:input,然后将file-1和file-2放入input目录中就可以运行:java WordCount input output,最终会在当前目录下创建output目录和结果文件的。当然如果用户遵循官方或者书上的例子用bin/hadoop -jar命令来运行的话,搜索input的起点就成了HDFS文件系统内部,需要先把input和file-1,file-2先用HDFS命令上传到HDFS中去。(如果有集群环境的话,可以file-1和file-2放在不同的节点机器上来观察效果)
其实MapReduce的模型简单易懂,但是运用起来还是很多经验和讲究的。比如如果要做到对海量同源数据进行分析和归类:例如淘宝网需要分析购买某种商品的用户还喜欢购买哪些别的商品,帮助用户找到志趣相投的其他用户或者卖家。在使用MapReduce进行数据分析挖掘的时候,如何选取key是相当重要的,key选择得当,可以让数据本身得到合并并在reduce中来进行统计。这就有点像SQL语句中的表连接,如果选取join的column对于分析的效率和结果集的大小、正确性都是至关重要的。