MapReduce
MapReduce的介绍
定义
MapReduce是一个分布式运算程序的编程框架,是用户开发,“基于Hadoop的数据分析应用”的核心框架
MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上
优点
易于编程MapReduce易于编程,它简单的实现一些接口,就可以完成一个分布式程序
良好的扩展性当计算资源不能得到满足时,就可以通过简单的增加机器来扩展它的计算能力
高容错性如果其中一台机器挂了,他可以把上面的计算任务转移到另一个节点上运行,不至于这个任务运行失败,而且这个过程不需要人工参与,而完全是有Hadoop内部完成的
适合海量数据离线处理可以实现上千台服务器集群并发工作,提高数据处理能力
缺点
不擅长实时计算MapReduce无法像MySQL一样,在毫秒或者秒级内返回结果
不擅长流式计算l流式计算的数据是动态的,而MapReduce的输入数据集是静态的,不能动态变化,这是因为MapReduce自身设计特点决定了数据源必须是静态的
不擅长DAG(有向图)计算不是不能做,而是每个MapReduce作业的输出结果都会写入到磁盘,会造成大量的磁盘IO,导致性能非常低下
MapReduce核心思想
1)分布式的运算程序往往需要分成至少2个阶段。
2)第一个阶段的MapTask并发实例,完全并行运行,互不相干。
3)第二个阶段的ReduceTask并发实例互不相干,但是他们的数据依赖于上一个阶段的所有MapTask并发实例的输出。
4)MapReduce编程模型只能包含一个Map阶段和一个Reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个MapReduce程序,串行运行。
总结:分析WordCount数据流走向深入理解MapReduce核心思想。
MapReduce进程
一个完整的MapReduce程序在分布式时有三类实例进程
- MrAppMaster:负责整个程序的过程调度即状态协调
- MapTask:负责Map阶段的整个数据处理流程
- ReduceTask:负责Reduce的阶段的整个数据处理流程
常用数据序列化类型
编程规范
三部分:Mapper、Reduce、Driver
Mapper阶段
- . 用户自定义的Mapper要继承自己的父类
- Mapper的输入数据是KV对的形式(KV的类型可自定义)
- Mapper中的业务逻辑写在map()方法中
- Mapper的输出数据十一KV对的形式(KV的类型可自定义)
- map()方法(MapTask进程)对每一个<K,V>调用一次
Reduce阶段 - 用户自定义的Reduce要继承自己的父类
- Reducer的输入数据类型对应Mapper的输出数据类型,也是KV
- Reducer的业务逻辑写在reduce()方法中
- ReduceTask进程对每一组相同的<K,V>调用一次reduce()方法
Driver阶段 - 相当于YARN集群的客户端,用于提交整个程序到YARN集群,提交的是封装了MapReduce程序相关运行参数的job对象
案例操作
1.需求
在给定的文本文件中统计输出每一个单词出现的总次数
2.具体步骤
先创建一个Maven project
在pom.xml文件中添加如下依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</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>
在项目的src/main/resources目录下,新建一个文件,命名为“log4j.properties”,在文件中填入。
log4j.rootLogger=INFO, 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/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
代码实现
WcMapper类
package com.atguigu.wordcount;
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;
public class WcMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
private Text word = new Text();
private IntWritable one = new IntWritable(1);
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//拿到这一行数据
String line = value.toString();
//按照空格切分数据
String[] words = line.split(" ");
//遍历数组,把单词变成(word,1)的形式交给框架
for(String word:words){
this.word.set(word);
context.write(this.word,this.one);
}
}
}
WcReduce类
package com.atguigu.wordcount;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class WcReducer extends Reducer<Text, IntWritable,Text,IntWritable> {
private IntWritable total = new IntWritable();
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
//做累加
int sum =0 ;
for (IntWritable value : values) {
sum += value.get();
}
//包装结果并输出
total.set(sum);
context.write(key,total);
}
}
WcDriver(看似复杂,但套路化严重)
package com.atguigu.wordcount;
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;
public class WcDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//1、获取一个job实例
Job job = Job.getInstance(new Configuration());
//2、设置我们的类路径
job.setJarByClass(WcDriver.class);
//3、设置Mapper和Reducer
job.setMapperClass(WcMapper.class);
job.setReducerClass(WcReducer.class);
//4、设置Mapper和Reducer的输出类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//5、设置输入输出数据
FileInputFormat.setInputPaths(job,new Path(args[0]));
FileOutputFormat.setOutputPath(job,new Path(args[1]));
//6、提交我们的Job
boolean b = job.waitForCompletion(true) ;
System.exit(b ? 0:1);
}
}
测试
本地测试,先指明第一个参数,第二个参数
然后运行,在本地查看运行结果
集群测试
①用maven打jar包,需要添加的打包插件依赖
②将程序打成jar包,然后拷贝到Hadoop集群中
步骤详情:右键->Run as->maven install。等待编译完成就会在项目的target文件夹中生成jar包。如果看不到。在项目上右键-》Refresh,即可看到。修改不带依赖的jar包名称为wc.jar,并拷贝该jar包到Hadoop集群
③启动Hadoop集群
④执行WordCount程序
[atguigu@hadoop102 software]$ hadoop jar wc.jar
com.atguigu.wordcount.WordcountDriver /user/atguigu/input /user/atguigu/output