hadoop(三)
1.对MapReduce的认识
MapReduce是运行在yarn上面的一个分布式运算框架,它是用来解决海量的分布式运算的.对于MapReduce来说,我们可以把它分成两部分来看:
Map:负责分,就是把复杂的任务分解为若干个"简单的任务"即
- 把数据或者计算的规模相对于任务要大大的缩小
- 会把任务分配到存放到所需数据的节点上面运行(也就是就近原则)
- 这些任务可以并行计算
Reduce:负责对map阶段的汇总
1.1 MapReduce的设计思想
mapreduce的核心功能是将用户编写的业务逻辑代码和自带的默认组件整合成一个完整的分布式运算程序,并运行在一个hadoop集群上面.
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 设计并提供了统一的计算框架,为我们隐藏了绝大多数系统层面的处理细节.它通过抽象模型和计算框架把把需要做什么(what
need to do)与具体怎么做(how to do)分开了,给我们提供一个抽象和高层的编程接口和框架。我们仅需要关心其应用层的具体计算问题,仅需编写少量的处
理应用本身计算问题的程序代码。如何具体完成这个并行计算任务所相关的诸多
系统层细节被隐藏起来,交给计算框架去处理:从分布代码的执行,到大到数千小
到单个节点集群的自动调度使用。
1.2 MapReduce的框架结构
一个完整的 mapreduce 程序在分布式运行时有三类实例进程:
- MRAppMaster:负责整个程序的过程调度及状态协调
- MapTask:负责 map 阶段的整个数据处理流程
- ReduceTask:负责 reduce 阶段的整个数据处理流程
1.3 简单的案例
在给定的文本文件中统计每一个单词出现的总次数
map阶段的
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 WorldCountMapper extends Mapper<LongWritable, Text,Text,IntWritable>{ //map方法的生命周期: 框架每传一行数据就被调用一次 //key 这一行起始点在文件中的偏移量 // value 这一行的内容 @Override protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException { //将拿到的一行数据转换成String String line = value.toString(); String[] words = line.split(" "); //遍历数组,输出<单词,1> for (String word : words) { context.write(new Text(word),new IntWritable(1)); } } }
reduce阶段的
import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer; public class WorldCountReducer extends Reducer<Text,IntWritable,Text,IntWritable>{ //生命周期:框架每传递进来一个kv数组,reduce方法就被调用一次 @Override protected void reduce(Text key, Iterable<IntWritable> value, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException { //定义一个计数器 int count=0; for (IntWritable intWritable : value) { count+=intWritable.get(); } context.write(key, new IntWritable(count)); } }
下面这个是总的调控
public class WorldCountDriver { //把业务逻辑相关的信息描述成一个job对象 //把描述好的job提交给集群运行 public static void main(String[] args) throws Exception { Configuration conf=new Configuration(); // conf.set("mapreduce.framework.name","local"); Job wordJob=Job.getInstance(conf); //指定这次job运行带的主类 wordJob.setJarByClass(WorldCountDriver.class); //指定本次job的具体的mapper reduce实现类 wordJob.setMapperClass(WorldCountMapper.class); wordJob.setReducerClass(WorldCountReducer.class); //指定本次job map阶段的输出数据类型 wordJob.setMapOutputKeyClass(Text.class); wordJob.setMapOutputValueClass(IntWritable.class); //指定本次job reduce阶段的输出数据类型 也就是整个mr任务的最终输出类型 wordJob.setOutputKeyClass(Text.class); wordJob.setOutputValueClass(IntWritable.class); //指定本次job待处理数据的目录, 和程序执行完输出结果存放的目录 FileInputFormat.setInputPaths(wordJob,"F:\\test\\word\\input"); FileOutputFormat.setOutputPath(wordJob, new Path("F:\\test\\word\\Output")); //提交本次job boolean b=wordJob.waitForCompletion(true); System.exit(b?0:1); } }
下面的是我在pom.xml中的配置
<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> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.13</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.5</version> </dependency> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</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>worldCount.WorldCountDriver</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> <verbal>true</verbal> </configuration> </plugin> </plugins> </build>
2 MapReduce处理流程
2.1 map任务执行过程
- 第一个阶段是把输入目录下文件按照一定的标准逐个进行逻辑切片,形成切片计划.默认情况下, ,Split size = Block size(在hadoop2的版本中,块的大小为128M)。每一个切片由一个MapTask 处理。(getSplits)
- 第二阶段是对切片中的数据按照一定的规则解析成<key,value>对。默认规
则是把每一行文本内容解析成键值对。key 是每一行的起始位置(单位是字
节),value 是本行的文本内容。(TextInputFormat) - 第三阶段是调用 Mapper 类中的 map 方法。上阶段中每解析出来的一个
<k,v>,调用一次 map 方法。每次调用 map 方法会输出零个或多个键值对。 - 第四阶段是按照一定的规则对第三阶段输出的键值对进行分区。默认是只
有一个区。分区的数量就是 Reducer 任务运行的数量。默认只有一个
Reducer 任务 - 第五阶段是对每个分区中的键值对进行排序。首先,按照键进行排序,对
于键相同的键值对,按照值进行排序。比如三个键值对<2,2>、<1,3>、<2,1>,键和值分别是整数。那么排序后的结果是<1,3>、<2,1>、<2,2>。如果有第六阶段,那么进入第六阶段;如果没有,直接输出到文件中 - 第六阶段是对数据进行局部聚合处理,也就是 combiner 处理。键相等的键
值对会调用一次 reduce 方法。经过这一阶段,数据量会减少。
2.2reduce任务执行过程
- 第一阶段是 Reducer 任务会主动从 Mapper 任务复制其输出的键值对。
Mapper 任务可能会有很多,因此 Reducer 会复制多个 Mapper 的输出。 - 第二阶段是把复制到 Reducer 本地数据,全部进行合并,即把分散的数据
合并成一个大的数据。再对合并后的数据排序 - 第三阶段是对排序后的键值对调用 reduce 方法。键相等的键值对调用一次
reduce 方法,每次调用会产生零个或者多个键值对。最后把这些输出的键
值对写入到 HDFS 文件中
3 MapReduce的序列化
序列化(Serialization)是指把结构化对象转化为字节流。
反序列化(Deserialization)是序列化的逆过程。把字节流转为结构化对象
当要在进程间传递对象或持久化对象的时候,就需要序列化对象成字节流,反之当要将接收到或从磁盘读取的字节流转换为对象,就要进行反序列化.
Java 的序列化(Serializable)是一个重量级序列化框架,一个对象被序列
化后,会附带很多额外的信息(各种校验信息,header,继承体系…),不便于
在网络中高效传输;所以,hadoop 自己开发了一套序列化机制(Writable),精
简,高效。不用像 java 对象类一样传输多层的父子关系,需要哪个属性就传输
哪个属性值,大大的减少网络传输的开销
来源:https://www.cnblogs.com/zhuguangzhe/p/8010548.html