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);
} }