1MapReduce定义
2MapReduce优缺点
3MapReduce核心思想
4MapReduce执行流程
5MapReduce的shuffle
6MapReduce案例
1定义
Mapreduce是一个分布式运算程序的编程框架,是用户开发“基于hadoop的数据分析应用”的核心框架。
它的核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个hadoop集群上。
总结:MapReduce就是Hadoop给我们提供的一个分布式运算程序编程框架,在此基础上我们可以开发自己的业务逻辑代码完成我们的计算任务,它会并行的运行在hadoop集群上。
2优缺点
优点:
.高容错:它的设计初衷就是让程序能够部署在廉价的PC机上,这样的话就需要具有很高的容错性,如果其中一台机器挂了,它可以把计算任务转移到另一台机器上运行,而不至于一台机器挂了程序就失败了。整个过程都不需要人为干涉,由hadoop自己在内部完成
.扩展性:当资源得不到满足的时候可以通过增加机器数量来扩展计算能力
.易于编程:它简单的实现一些接口,就可以完成一个分布式程序,这个分布式程序可以分布到大量廉价的PC机器上运行。就是因为这个特点使得MapReduce编程变得非常流行。
.适合海量数据的离线处理:适合做海量数据的离线处理
缺点:
.延时高:无法像关系型数据库在一样毫秒内返回计算结果
.流式计算:流式计算的输入数据是动态的,而MapReduce的输入数据集是静态的,不能动态变化。这是因为MapReduce自身的设计特点决定了数据源必须是静态的。
.DAG计算:多个应用程序存在依赖关系,后一个应用程序的输入为前一个的输出。在这种情况下,MapReduce并不是不能做,而是使用后,每个MapReduce作业的输出结果都会写入到磁盘,会造成大量的磁盘IO,导致性能非常的低下。
3核心思想
1分布式的运算程序往往需要分成至少2个阶段。
2第一个阶段的maptask并发实例,完全并行运行,互不相干。
3第二个阶段的reduce task并发实例互不相干,但是他们的数据依赖于上一个阶段的所有maptask并发实例的输出。
4MapReduce编程模型只能包含一个map阶段和一个reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个mapreduce程序,串行运行。
4执行流程
1客户端会提交一个mr的jar包给jobClient
2jobClien会通过RPC和JobTracker通信,返回一个jar包的地址(hdfs)和jobId,jobId唯一,用于表示该job
3Client将jar包写入hdfs(地址+jobId)
4开始提交任务
5jobTracker初始化任务
6读取hdfs上要处理的文件,开始计算输入切片,每一个切片对应一个maptask,切片存储的是描述信息
7taskTracker通过心跳机制领取到任务(任务的描述信息)
8下载所需的jar,配置文件等
9TaskTracker启动一个java Child子进程
10执行具体任务
5shuffle机制
无论是mr的shuffle还是spark的shuffle都是map结束到reduce的过程,shuffle就像是桥梁一样
maptask调用mapper方法将数据存到环形缓冲区中,在写入的过程中进行paritition,也就是对每一个k/v对都增加一个partition属性序列化到缓冲区默认大小为100M,阈值为0.8。
当写入的数据量达到阈值就会开始溢写操作到临时文件中,在写入前会根据key进行归并排序(节省空间降低I/O)
溢写过程按轮询方式将缓冲区内容写到指定目录中,后会对所有的spill文件(临时文件)进行归并产生最终的输出文件,此时的归并是将所有spill文件中相同partition合并,再根据key在进行一次排序(自此map端结束)
map结束后最终生成的文件也会存储在TaskTracker能访问的位置,reduceTask通过RPC从jobTracker哪里获取是否完成的信息,如果完成reduce端开始
reduceTask从mapTask的结果文件中拉取相应分区的数据,根据偏移量取,reduce拉取数据的时候会按自己的逻辑再合并排序,将最终结果写到hdfs上
6案例
入门WordCount案例
读取txt文件中的数据
按行读取之后每一个单词都赋值
最终聚合之后达到统计单词数的目的
代码如下
导入pom依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.2</version>
</dependency>
</dependencies>
打日志在resources下新建一个log4j.properties
log4j.rootLogger=debug, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/mr.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
编写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>{
Text k = new Text();
IntWritable v = new IntWritable(1);
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
// 1 获取一行
String line = value.toString();
// 2 切割
String[] words = line.split(" ");
// 3 输出
for (String word : words) {
k.set(word);
context.write(k, v);
}
}
}
编写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>{
int sum;
IntWritable v = new IntWritable();
@Override
protected void reduce(Text key, Iterable<IntWritable> value,
Context context) throws IOException, InterruptedException {
// 1 累加求和
sum = 0;
for (IntWritable count : value) {
sum += count.get();
}
// 2 输出
v.set(sum);
context.write(key,v);
}
}
编写driver类
import java.io.IOException;
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 WordcountDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
// 1 获取配置信息以及封装任务
Configuration configuration = new Configuration();
Job job = Job.getInstance(configuration);
// 2 设置jar加载路径
job.setJarByClass(WordcountDriver.class);
// 3 设置map和reduce类
job.setMapperClass(WordcountMapper.class);
job.setReducerClass(WordcountReducer.class);
// 4 设置map输出
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
// 5 设置Reduce输出
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// 6 设置输入和输出路径
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 7 提交
job.waitForCompletion(true);
}
}