1. 什么是MapReduce MapReduce是Google提出的一种分布式计算模型,用于大规模数据集(大于1TB)的并行运算,解决海量数据的计算问题。主要阶段是Map(映射)和Reduce(归约)。 (1)Map任务:

  • 读取输入文件内容,解析成key、value对。对输入文件的每一行,解析成key、value对。每一个键值对调用一次map函数
  • 写自己的逻辑,对输入的key、value处理,转换成新的key、value输出
  • 对输出的key、value进行分区
  • 对相同分区的数据,按照key进行排序(默认按照字典顺序进行排序),分组。相同key的value放到一个集合中。
  • (可选)分组后的数据进行归约。
  • 注意:在MapReduce中,Mapper可以单独存在,但是Reducer不能单独存在

(2)Reducer任务:

  • 对多个map任务的输出,按照不同的分区,通过网络copy到不同的reduce节点。这个过程并不是map将数据发送给reduce,而是reduce主动去获取数据。--- Reducer的个数 >= 分区的数量
  • 对多个map任务的输出进行合并、排序。写reduce函数自己的逻辑,对输入的key、value处理,转换成新的key、value输出。
  • 把reduce的输出保存到文件中。

2. MapReduce的执行原理 (1)分片、格式化数据源 分片:将数据源文件划分为⼤⼩相等的⼩数据块⼉。 格式化:将数据整理为<Key,Value>格式,key为偏移量,value为每一行的内容 (2)执行MapTask 每个Map任务有一个对应的内存缓冲区,中间结果会优先写入到缓冲区中。如果缓冲区将满,则将内容写入至磁盘。当所有的Map进程结束后,会将结果统一合并为一个溢写文件。 (3)执行Shuffle过程 对于MapTask处理的结果进行整理,按key进行分区和排序,输出给Reduce阶段。 (4) 执行ReduceTask 接收到<Key,{ValueList}>的数据,在reduce⽅法中⾃定义处理的逻辑,得到最终结果,并写⼊文件。 (5)写入文件

3. MapReduce执行流程 (1)做job环境信息的收集,比如各个组件类,输入输出的kv类型等,检测是否合法 * 检测输入输出的路径是否合法 * run job:客户端提交一个mr的jar包给JobClient(提交方式:hadoop jar ...。 (2)JobClient通过RPC和ResourceManager进行通信,返回一个存放jar包的地址(HDFS)和jobId。jobID是全局唯一的,用于标识该job (3)client将jar包写入到HDFS当中(path = hdfs上的地址 + jobId) (4)开始提交任务(任务的描述信息,不是jar, 包括jobid,jar存放的位置,配置信息等等) (5)JobTracker进行初始化任务 (6)读取HDFS上的要处理的文件,开始计算输入切片,每一个切片对应一个MapperTask。注意,切片是一个对象,存储的是这个切片的数据描述信息;切块是文件块(数据块),里面存储的是真正的文件数据 (7)TaskTracker通过心跳机制领取任务(任务的描述信息)。切片一般和切块是一样的,即在实际开发中,切块和切片认为是相同的。在领取到任务之后,要满足数据本地化策略 (8)下载所需的jar,配置文件等。体现的思想:移动的是运算/逻辑,而不是数据 (9)TaskTracker启动一个java child子进程,用来执行具体的任务(MapperTask或ReducerTask) (10)将结果写入到HDFS当中 图解如下:

4.MapReduce代码的简单实现 Mapper代码:

import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
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,
IntWritable> {
 
 @Override
 protected void map(LongWritable key, Text value, Mapper<LongWritable, Text,
Text, IntWritable>.Context context)
 throws IOException, InterruptedException {
 // TODO WordCount的map函数
 // key:偏移量,value:每⼀⾏内容
 String line = value.toString();
 // 将每⾏的数据按照空格分割
 String[] words = line.split(" ");
 // 将数组中的每个单词,整理成<Word,1>的格式 -> 代表该单词出现⼀次
 for (String word : words) {
 context.write(new Text(word), new IntWritable(1));
 }
 } }

Reducer代码:

 import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
public class WordCountReducer extends Reducer<Text, IntWritable, Text,
IntWritable> {
 
 @Override
 protected void reduce(Text key, Iterable<IntWritable> values,
 Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws
IOException, InterruptedException {
 int count = 0;// 定义⽤于存放结果的变量
 for (IntWritable value : values) {
 count += value.get();// 将value列表中的元素取出,进⾏累加
 }
 context.write(key, new IntWritable(count));
 } }

Master代码:

 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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class WordCountMaster {
 public static void main(String[] args) throws Exception {
 // TODO wordcount的启动类
 Configuration conf = new Configuration();
 conf.set("mapreduce.framework.name", "local");// local:本地模式
 Job job = Job.getInstance();// 实例化对象:在new之前传⼊参数或做其他操作
 job.setJarByClass(WordCountMaster.class);// class相当与Java中的类对象(类的描述
信息:路径、内部结构)
 // 指定Mapper阶段要执⾏的map函数所在的类
 job.setMapperClass(WordCountMapper.class);
 // Mapper阶段输出的数据结构的类对象
 job.setMapOutputKeyClass(Text.class);
 job.setMapOutputValueClass(IntWritable.class);
 // 指定Reducer阶段要执⾏的reduce函数所在的类
 job.setReducerClass(WordCountReducer.class);
 // Reducer阶段输出的数据结构的类对象(最终结果)
 job.setOutputKeyClass(Text.class);
 job.setOutputValueClass(IntWritable.class);
 // 本地路径 -> file:///xxx/xxx
 // HDFS路径 -> hdfs://ip:port/xxx -> /xxx
 FileInputFormat.setInputPaths(job, new Path(args[0]));
 FileOutputFormat.setOutputPath(job, new Path(args[1]));
 // 提交运算流程,true:显示详细信息,返回值代表最终的运⾏状态
 boolean result = job.waitForCompletion(true);
 System.out.println(result);
 } }