# 直观理解 统计一堆牌中有多少张黑桃,最简单的方法是一张一张去数。而 MapReduce 的方法是: 1. 把这堆牌分配给多个玩家; 2. 让每个玩家数自己手中有多少张黑桃; 3. 把所有玩家数出的黑桃数加起来就是最后的结果。 通过让多个玩家来并行地进行统计,MapReduce 可以将统计时间大大缩短。 # 大数据 从大量数据中快速提取出有价值的信息。注意到这里的数据不一定需要同种类型的数据,可以是多种多样的数据,包括非结构化数据、半结构化数据以及结构化数据。 # 基本原理 MapReduce 用来对大数据中的海量数据进行离线分析。 MapReduce 分为两个阶段:Map 阶段和 Reduce 阶段。 在下图中,file 包含多个 block,每个块代表一个海量数据。file 被划分为多个 split,每个分片由一个 Mapper Task 去处理。Map 过程中输入的是 [K1, V1] 数据,这种是一种键值对形式的数据,键为泛型 K1 的类型,值为泛型 V1 的类型。输入也是一个泛型的键值对 [K2, V2]。Shuffle 过程主要是对 Map 阶段输出的键值对进行整合,将键相同的键值对整合到一组中,得到 [K2, {V2,...}] 的整合数据,作为 Reudce 阶段的输入。Reduce 处理完之后得到 [K3, V3] 键值对,Hadoop 会将输出的数据存到分布式文件系统 HDFS 中。 ![](index_files/e2026020-d669-4faa-9e16-ca726619bb9f.jpg) # WordCount 实战 ## 开发环境配置 创建 Maven 项目,在 pom.xml 中添加以下依赖: ```              3.0.0                                org.apache.hadoop             hadoop-common             ${hadoopVersion}                               org.apache.hadoop             hadoop-hdfs             ${hadoopVersion}                               org.apache.hadoop             hadoop-mapreduce-client-core             ${hadoopVersion}                               org.apache.hadoop             hadoop-client             ${hadoopVersion}               ``` ## 文本 ```html the weather is good today is good good weather is good today has good weather ``` 将文本的每一行分成一片,每一片的数据提供给 Mapper 进行处理,因此需要 4 个 Mapper。其中 K1 为行号,V1 位该行的字符串。 ```html Split-0: [0, "the weather is good] Split-1: [1, "today is good"] Split-2: [2, "good weather is good"] Split-3: [3, "today has good weather] ``` ## Mapper Mapper 类需要提供四个泛型,分别为 K1, V1, K2, V2。 ```java class WordCountMappper extends Mapper {     @Override     protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {         String line = value.toString();         String[] words = line.split(" ");         for (String word : words) {             context.write(new Text(word), new IntWritable(1));         }     } } ``` ```html Mapper-0: ["the", 1], ["weather", 1], ["is", 1], ["good", 1] Mapper-1: ["today", 1], ["is", 1], ["good", 1] Mapper-2: ["good", 2], ["weather", 1], ["is", 1] Mapper-3: [today", 1], ["has", 1], ["good", 1], ["weater", 1] ``` ## Shuffle 排序并将拥有相同键的数据整合。 ```html ["good", {1, 1, 2, 1}] ["has", {1}] ["is", {1, 1, 1}] ["the", {1}] ["today", {1, 2}] ["weater", {1,1}] ``` ## Reducer Reducer 同样要指定 4 个泛型:K2, V2, K3, V3 ```java public class WordCountReducer extends Reducer {     @Override     protected void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException {         Integer count = 0;         for (IntWritable value : values) {             count += value.get();         }         context.write(key, new IntWritable(count));     } } ``` ```html Reducer-0: ["good", 5] Reducer-1: ["has", 1] Reducer-2: ["is", 3] Reducer-3: ["the", 1] Reducer-4: ["today", 2] Reducer-5: ["weather", 3] ``` ## Job 将项目打包成 jar 包后使用 Hadoop 来运行。 ```java public class WordCountMapReduce {     public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {         Configuration conf = new Configuration(); // 新建 Job         Job job = Job.getInstance(conf, "wordcount");         job.setJarByClass(WordCountMapReduce.class);         job.setMapperClass(WordCountMappper.class);         job.setReducerClass(WordCountReducer.class);         // 设置 Mapper 和 Reducer 的 Key Value 类型         job.setMapOutputKeyClass(Text.class);         job.setOutputValueClass(IntWritable.class);         job.setOutputKeyClass(Text.class);         job.setOutputValueClass(IntWritable.class); // 设置 HDFS 路径         FileInputFormat.setInputPaths(job, new Path("hdfs://domain:8020/words"));         FileOutputFormat.setOutputPath(job, new Path("hdfs://domain:8020/words")); // 运行         boolean b = job.waitForCompletion(true);         if (!b) {             System.err.println("This task has failed!!!");         }     } } ``` # 参考资料 - [Hadoop MapReduce 入门](http://www.jikexueyuan.com/course/2686.html)