初识MapReduce
文章目录
1.MapReduce计算模型介绍
1.1 理解MapReduce思想
MapReduce的思想核心是“分而治之”,适用于大量复杂的任务处理场景(大规模数据处理场景)。
Map负责“分”,即把复杂的任务分解为若千个“简单的任务”来并行处理。可以进行拆分的前提是这些小任务可以并行计算,彼此间几乎没有依赖关系。
Reduce负责“合”,即对map阶段的结果进行全局汇总。
这两个阶段合起来正是MapReduce思想的体现。
1.2 Hadoop&MapReduce设计构思
MapReduce是一个分布式运算程序的编程框架,核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在Hadoop集群上。
既然是做计算的框架,那么表现形式就是有个输入( input),MapReduce 操作这个输入(input), 通过本身定义好的计算模型,得到一个输出(output)。对许多开发者来说,自己完完全全实现一个并行计算程序难度太大,而MapReduce就是一-种简化并行计算的编程模型,降低了开发并行应用的入门门槛。
Hadoop MapReduce 构思体现在如下的三个方面:
- 如何对付大数据处理:分而治之
对相互间不具有计算依赖关系的大数据,实现并行最自然的办法就是采取分而治之的策略。并行计算的第一个重要问题是如何划分计算任务或者计算数据以便对划分的子任务或数据块同时进行计算。不可分拆的计算任务或相互间有依赖关系的数据无法进行并行计算! - 构建抽象模型: Map和Reduce
MapReduce借鉴了函数式语言中的思想,用Map和Reduce两个函数提供了高层的并行编程抽象模型。
Map:对一组数据元素进行某种重复式的处理;
Reduce:对Map的中间结果进行某种进一步的结果整理。
MapReduce中定义了如下的Map和Reduce两个抽象的编程接口,由用户去编程实现:
map:(k1;v1)→[(k2; v2)]
reduce: (k2; [v2])→[(k3;v3)]
Map和Reduce为程序员提供了一个清晰的操作接口抽象描述。通过以上两个编程接口,大家可以看出MapReduce处理的数据类型是<key,value>键值对。 - 统一构架,隐藏系统层细节
如何提供统一的计算框架,如果没有统一封装底层细节,那么程序员则需要考虑诸如数据存储、划分、分发、结果收集、错误恢复等诸多细节:为此,MapReduce设计并提供了统一的计算框架, 为程序员隐藏了绝大多数系统层面的处理细节。
MapReduce最大的亮点在于通过抽象模型和计算框架把需要做什么(what need to do)与具体怎么做(how to do)分开了,为程序员提供一个抽象和高层的编程接口和框架。程序员仅需要关心其应用层的具体计算问题,仅需编写少量的处理应用本身计算问题的程序代码。如何具体完成这个并行计算任务所相关的诸多系统层细节被隐藏起来,交给计算框架去处理:从分布代码的执行,到大到数千小到单个节点集群的自动调度使用。
1.3 MapReduce框架结构
一个完整的mapreduce程序在分布式运行时有三类实例进程:
1、MRAppMaster:负责整个程序的过程调度及状态协调
2、MapTask:负责map阶段的整个数据处理流程
3、ReduceTask: 责reduce阶段的整个数据处理流程
2.MapReduce编程规范及示例编写
2.1 编程规范
(1)用户编写的程序分成三个部分: Mapper, Reducer, Driver (提交运行mr程序的客户端)
(2) Mapper的输入数据是KV对的形式(KV 的类型可自定义)
(3) Mapper的输出数据是KV对的形式(KV 的类型可自定义)
(4) Mapper中的业务逻辑写在map()方法中
(5) map() 方法(maptask 进程)对每一个<K, V>调用一次
(6)Reducer的输入数据类型对应Mapper的输出数据类型,也是KV
(7) Reducer的业务逻辑写在reduce()方法中
(8) Reducetask 进程对每一组相同k的<k, v>组调用一次reduce()方法
(9)用户自定义的Mapper和Reducer都要继承各自的父类
(10)整个程序需要一个Drvier来进行提交,提交的是一个描述了各种必要信息的job对象
2.2WordCount示例编写
需求:在一堆给定的文本文件中统计输入每个单词出现的总次数。
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.itcast</groupId>
<artifactId>example-mr</artifactId>
<version>1.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>aliyun-repos</id>
<url>https://maven.aliyun.com/repository/central/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>aliyun-plugin</id>
<url>https://maven.aliyun.com/repository/central/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib</classpathPrefix>
<mainClass>cn.itcast.hadoop.mr.WordCountDriver</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 定义一个Mapper类
package cn.itcast.hadoop.mr;
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;
/**
* 这里就是Mapper阶段业务逻辑实现的类
* Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
*KEYIN:表示mapper数据输入的时候数据类型,在默认的读取数据的组件下,叫InputFormat,他的行为是一行一行的读取待处理的数据
* 读取一行,返回一行给mr程序,这种情况下,KININ就代表每一行的起始偏移量,因此数据类型为Long
*VALUEIN:表示mapper数据输入的时候,VALUE的数据类型,在默认的读取数据的组件下,VALUEIN就表示读取的这一行内容,因此数据类型为String
*KEYOUT表示mapper 数据输出的时候key的数据类型在本案例当中 输出的key是单词因此数据类型是 String
* VALUEOUT表示mapper数据输出的时候value的数据类型在本案例当中 输出的key是 单词的次数因此数据类型是 Integer
* 这里所说的数据类型String Long都是jdk自带的类型在序列化的时候 教率低 下因此hadoop自 己封装一套 数据类型
* long---->LongWritable
* Str ing-->Text
* Integer--->Intwritable
* null-->NulLWritable
*/
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
/**
*这里就是Mapper阶段具体的业务逻辑实现方法 ,该方法的调用取决于读取数据组件有没有给mr传入数据
* 如果有的话,每传入一个<k,v>对,该方法就会被调用一次
* @param key
* @param value
* @param context
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//拿到传入进来的一行内容,把数据类型转化为String
String line = value.toString();
//将这一行内容按照分隔符进行切分,切分为一个个单词
String[] words = line.split(" ");
//遍历数组,每出现一个单词就标记数字1<单词,1>
for (String word : words){
//使用mr程序的上下文context,将mapper阶段处理的数据发送出去
//作为reduce阶段的输入数据
context.write(new Text(word),new IntWritable(1));
//hadoop hadoop spark <hadoop,1><hadooop,1><spark,1>
}
}
}
- 定义一个Reducer类
package cn.itcast.hadoop.mr;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/**
* 这里是mr程序reducer阶段处理的类
* KEYIN : reducer阶段输入的数据KEY类型,对应Mapper的输出key类型,本案例中是单词Text
* VALUEIN : reducer阶段输入的数据VALUE类型,对应Mapper的输出VALUE类型,本案例中是单词个数 IntWritable
* KEYOUT : reducer阶段输出的数据KEY类型,本案例中是单词Text
* VALUEOUT: reducer阶段输出的数据VALUE类型,本案例中是单词总个数 IntWritable
*/
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
/**
* 这里是reduce阶段具体业务类的实现方法
* @param key
* @param values
* @param context
* @throws IOException
* @throws InterruptedException
*
* reduce接收所有来自,map阶段处理的数据之后,按照key的字典序进行排序
*<hello, 1> <hadoop, 1><spark, 1> <hadoop, 1>
* 排序后:
* <hadoop, 1><hadoop, 1><hello, 1><spark, 1>
* 按照key是否相同作为一组去调用reduce方法
* 本方法的key就是这一组kv对的共同key
* 把这一组所有的v作为一个迭代器传去reduce方法
*
* <hadoop,[1,1]>
*/
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
//定义一个计数器
int count = 0;
//遍历一组迭代器,把每一个数量1累加起来就构成了单词的总个数<hadoop,[1,1]>
for (IntWritable value : values){
count += value.get();
}
//最终结果输出
context.write(key,new IntWritable(count));
}
}
- 定义一个主类,用来描述job并提交job
package cn.itcast.hadoop.mr;
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;
/**
* 这个类就是mr程序运行时候的主类
* 本类中组装了一些程序运行所需的信息
* 比如:使用哪个Mapper类,哪个Reducer类,输入数据在哪,输出到哪
*/
public class WordCountDriver {
public static void main(String[] args) throws Exception {
//通过Job来封装本次mr的相关信息
Configuration conf = new Configuration();
// conf.set("mapreduce.framework.name","local");//默认本地模式,不写这行也行
Job job = Job.getInstance(conf);
//指定本次mrjobjar包的运行主类
job.setJarByClass(WordCountDriver.class);
//指定本次mr 所用的Mapper,Reducer类
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
//指定本次mr Mapper阶段的输出KV类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//指定本次mr Reducer总阶段的输出KV类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// job.setNumReduceTasks(3);
//指定本次mr 输入的数据路径 和最终输出结果存放位置
FileInputFormat.setInputPaths(job,"E:\\wordcount\\input");
FileOutputFormat.setOutputPath(job,new Path("E:\\wordcount\\output"));
// job.submit();//非本地
//提交程序 并且监控打印程序执行情况
boolean b = job.waitForCompletion(true);
System.exit(b?0:1);
}
}
3.MapReduce程序运行模式
3.1本地运行模式
(1) mapreduce 程序是被提交给LocalJobRunner在本地以单进程的形式运行
(2)而处理的数据及输出结果可以在本地文件系统,也可以在hdfs上
(3)怎样实现本地运行?写一个程序,不要带集群的配置文件
本质是程序的conf 中是否有mapreduce. framework. name=local以及
yarn. resourcemanager. hostname参数
(4)本地模式非常便于进行业务逻辑的debug,只要在eclipse中打断点即可
3.2集群运行模式
(1)将mapreduce程序提交给yarn集群,分发到很多的节点上并发执行
(2)处理的数据和输出结果应该位于hdfs文件系统
(3)提交集群的实现步骤:
将程序打成JAR包,然后在集群的任意一个节点上用hadoop命令启动
hadoop jar wordcount.jar cn. itcast. bigdata. mrsimple. WordCountDriver args