MapReduce 是一个分布式运算程序的编程框架,是用户开发基于Hadoop的数据分析应用的核心框架。
MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发的运行的在一个Hadoop集群上
1)MapReduce 易于编程,它简单的实现一些接口,就可以完成一个分布式程序,这个分布式程序可以再大量的廉价的PC机器上运行。
2)良好的拓展性:当计算资源不能满足需求的时候,可以通过简单的增加机器来扩展它的计算能力。
3)高容错性:如果其中一台机器挂了,它可以把上面的计算任务转移到另外一个节点上运行,不至于这个任务运行失败,而且这个过程不需要人工参与,而完全是由Hadoop内部完成的。
4)适合PB级以上海量数据的离线处理:可以实现上千台服务器集群并发工作,提供数据处理能力。
不擅长事实计算,无法向MySQL一样,再秒级或毫秒级返回计算结果。
不擅长流式计算 (输入数据是动态的),而MapReduce的输入数据集是静态的,不能动态变化。
不擅长DAG(有向无环图)计算
(1)分布式的运算程序往往需要分成至少2个阶段。
(2)第一个阶段的MapTask并发实例,完全并行运行,互不相干。
(3)第二个阶段的ReduceTask并发实例互不相干,但是他们的数据依赖于上一个阶段的所有MapTask并发实例的输出。
(4)MapReduce编程模型只能包含一个Map阶段和一个Reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个MapReduce程序,串行运行。
一个完整的MapReduce程序在分布式运行时有三类实例进程:
(1)MrAppMaster:负责整个程序的过程调度及状态协调。
(2)MapTask:负责Map阶段的整个数据处理流程。
(3)ReduceTask:负责Reduce阶段的整个数据处理流程。
Java****类型 | Hadoop Writable****类型 |
---|---|
Boolean | BooleanWritable |
Byte | ByteWritable |
Int | IntWritable |
Float | FloatWritable |
Long | LongWritable |
Double | DoubleWritable |
String | Text |
Map | MapWritable |
Array | ArrayWritable |
Null | NullWritable |
用户编写的程序分成三个部分:Mapper、Reducer和Driver。(WordCount 案例)
WordCountMapper
package mapreducer;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
/**
* @author tccstart
* @create 2020-08-09 13:03
* Mapper
* <行偏移量,行的字符值,单词字符值,1>
*/
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
private Text k = new Text(); //这里又不能添加word,但是map有set方法
private IntWritable v = new IntWritable(1);
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//先把value转化成string, 这就获取了一行的字符串
String line = value.toString();
//把一行按照空格切成单词, String型数组接收
String[] words = line.split(" ");
//遍历单词数组
for (String word : words) {
//现在我们有单词了,怎么每次遍历弄成 , 的格式呢
//理解Context是什么, Context implements MapContext
//Context是继承于map的一个键值对, 我们可以看到context 有一个write方法,有两个形参Text,和 IntWritable
//因此我们要把 String型word转成text, 1转换成IntWritable
// Text k = new Text(word);
// IntWritable v = new IntWritable(1);
k.set(word); //用set方法给map中的key赋值
context.write(k,v); //这是我们想要的格式,但是每次都new对象很浪费内存
}
}
}
WordCountReducer
package mapreducer;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/**
* @author tccstart
* @create 2020-08-09 14:10
*
* 第一对 k-v 是mapper的输出k-v Text, LongWritable
* 第二对 k-v 是自己自定义的k-v Text, IntWritable ---- william 2 (单词,出现的次数)
*
* 此是进入reducer是什么样的数据呢? 统计出出现的单词,内部一个list集合收集了出现1,
*
*
* 究竟底层怎么帮我们做成了这种mapper输出呢?
*/
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
private IntWritable v = new IntWritable();
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
//所以我们需要遍历values的集合
int sum = 0;
for (IntWritable value : values) {
sum += value.get(); //取出每个 k-v 对中1相加
}
//等同于mapper中的context, 也是存储的键值对(Text IntWritable)
v.set(sum);
context.write(key, v);
}
}
WordCountDriver
package mapreducer;
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;
import java.io.IOException;
/**
* @author tccstart
* @create 2020-08-09 14:56
*
* 写一个Driver驱动类(main方法中),谨记7步
*/
public class WordCountDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//第一步:获取Job对象,用反射的方式
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
//第二步:关联本类
job.setJarByClass(WordCountDriver.class);
//第三步:关联mapper和reducer类
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
//第四步:设置Mapper输出类型(包含key 和 value)
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//第五步:设置最终输出类型(k-v)
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//第六步:设置输入和输出路径
//addInputPath(Job job, Path path) 需要两个形参
FileInputFormat.setInputPaths(job, new Path("D:\\TestData\\input\\inputword"));
FileOutputFormat.setOutputPath(job, new Path("D:\\TestData\\hadoop\\output"));
//第七步:提交job
boolean result = job.waitForCompletion(true);
System.exit(result ? 0 : 1);
}
}