MapReduce应用广泛的原因之一就是其易用性,提供了一个高度抽象化而变得非常简单的编程模型,它是在总结大量应用的共同特点的基础上抽象出来的分布式计算框架,在其编程模型中,任务可以被分解成相互独立的子问题。MapReduce编程模型给出了分布式编程方法的5个步骤:

  1. 迭代,遍历输入数据,将其解析成key/value对;
  2. 将输入key/value对映射map成另外一些key/value对;
  3. 根据key对中间结果进行分组(grouping);
  4. 以组为单位对数据进行归约;
  5. 迭代,将最终产生的key/value对保存到输出文件中。

下面就简要总结一下编程模型中用到的主要组件以及在其中的作用:

仍然以示例开始:

package hadoop;

import java.io.IOException;
import java.util.StringTokenizer;
import java.util.UUID; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.parquet.example.data.Group;
import org.apache.parquet.example.data.simple.SimpleGroupFactory;
import org.apache.parquet.hadoop.ParquetOutputFormat;
import org.apache.parquet.hadoop.example.GroupWriteSupport;
/**
* * <p>Title: ParquetNewMR</p> * <p>Description: </p> * @author zjhua * @date 2019年4月7日
*/
public class ParquetNewMR { /**
* map模型 * <p>Title: WordCountMap</p> * <p>Description: </p> * @author zjhua * @date 2019年4月23日
*/
public static class WordCountMap extends
Mapper<LongWritable, Text, Text, IntWritable> { private final IntWritable one = new IntWritable(1);
private Text word = new Text();
@Override
public void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String line = value.toString();
StringTokenizer token = new StringTokenizer(line);
while (token.hasMoreTokens()) {
word.set(token.nextToken());
context.write(word, one);
}
}
} /**
* reduce模型 * <p>Title: WordCountReduce</p> * <p>Description: </p> * @author zjhua * @date 2019年4月23日
*/
public static class WordCountReduce extends
Reducer<Text, IntWritable, Void, Group> {
private SimpleGroupFactory factory;
@Override
public void reduce(Text key, Iterable<IntWritable> values,
Context context) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
Group group = factory.newGroup()
.append("name", key.toString())
.append("age", sum);
context.write(null,group);
} @Override
protected void setup(Context context) throws IOException, InterruptedException {
super.setup(context);
factory = new SimpleGroupFactory(GroupWriteSupport.getSchema(context.getConfiguration()));
}
} public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
String writeSchema = "message example {\n" +
"required binary name;\n" +
"required int32 age;\n" +
"}";
conf.set("parquet.example.schema",writeSchema);
// conf.set("dfs.client.use.datanode.hostname", "true"); Job job = Job.getInstance(conf); // new Job()接口过期了
job.setJarByClass(ParquetNewMR.class);
job.setJobName("parquet"); String in = "hdfs://192.168.223.150:8020/user/hadoop1/wordcount/input";
String out = "hdfs://192.168.223.150:8020/user/hadoop1/pq_out_" + UUID.randomUUID().toString(); job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class); job.setOutputValueClass(Group.class); job.setMapperClass(WordCountMap.class); // Map实现类
job.setReducerClass(WordCountReduce.class); //Reduce实现类 job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(ParquetOutputFormat.class); FileInputFormat.addInputPath(job, new Path(in));
ParquetOutputFormat.setOutputPath(job, new Path(out));
ParquetOutputFormat.setWriteSupportClass(job, GroupWriteSupport.class); job.waitForCompletion(true);
}
}

1. InputFormat

主要用于描述输入数据的格式,提供数据切分功能,按照某种方式将输入数据且分成若干个split,确定map task的个数,以及为Mapper提供输入数据,给定某个split,让其解析成一个个key/value对。

InputFormat中的getSplits方法主要完成数据切分的功能,会尝试着将输入数据且分成numSplits个进行存储。InputSplit中只记录了分片的元数据信息,比如起始位置、长度以及所在的节点列表。

在Hadoop中对象的序列化主要用在进程间通信以及数据的永久存储。Client端会调用Job中的InputFormat中的getSplits函数,当作业提交到JobTracker端对作业初始化时,可以直接读取该文件,解析出所有InputSplit,并创建对应的MapTask。

而重要的方法就是getRecordReader,其返回一个RecordReader,将输入的InputSplit解析成若干个key/value对。MapReduce框架在Map Task执行过程中,不断地调用RecordReader对象中的方法,获取key/value对交给map函数处理,伪代码如下:

K1 key = input.createKey();
V1 value = input.createValue();
while(input.next(key, value)){
//invoke map()
}
input.close();

对于FileInputFormat,这是一个采用统一的方法对各种输入文件进行切分的InputFormat,也是比如TextInputFormat, KeyValueInputFormat等类的基类。其中最重要的是getSplits函数,最核心的两个算法就是文件切分算法以及host选择算法。

文件切分算法主要用于确定InputSplit的个数以及每个InputSplit对应的数据段。

在InputSplit切分方案完成后,就需要确定每个InputSplit的元数据信息: <file, start, length, host>,表示InputSplit所在文件,起始位置,长度以及所在的host节点列表,其中host节点列表是最难确定的。

host列表选择策略直接影响到运行过程中的任务本地性。Hadoop中HDFS文件是以block为单位存储的,一个大文件对应的block可能会遍布整个集群,InputSplit的划分算法可能导致一个InputSplit对应的多个block位于不同的节点上。

hadoop将数据本地性分成三个等级:node locality, rack locality和data center locality。在进行任务调度时,会依次考虑3个节点的locality,优先让空闲资源处理本节点的数据,其次同一个机架上的数据,最差是处理其他机架上的数据。

虽然InputSplit对应的block可能位于多个节点上,但考虑到任务调度的效率,通常不会将所有节点到InputSplit的host列表中,而是选择数据总量最大的前几个节点,作为任务调度时判断任务是否具有本地性的主要凭据。对于FileInputFormat设计了一个简单有效的启发式算法:按照rack包含的数据量对rack进行排序,在rack内部按照每个node包含的数据量对node排序,取前N个node的host作为InputSplit的host列表(N为block的副本数,默认为3)。

当InputSplit的尺寸大于block的尺寸时,MapTask不能实现完全的数据本地性,总有一部分数据需要从远程节点中获取,因此当使用基于FileInputFormat实现InputFormat时,为了提高Map Task的数据本地性,应该尽量使得InputSplit大小与block大小相同。(虽然理论上是这么说,但是这会导致过多的MapTask,使得任务初始时占用的资源很大)。

2. OutputFormat

OutputFormat主要用于描述输出数据的格式,能够将用户提供的key/value对写入特定格式的文件中。其中与InputFormat类似,OutputFormat接口中有一个重要的方法就是getRecordWriter,返回的RecordWriter接收一个key/value对,并将之写入文件。Task执行过程中,MapReduce框架会将map或reduce函数产生的结果传入write方法:

public void map(Text key, Text value, OutputCollector<Text, Text> output, Reporter reporter) throws IOException{
output.collect(newKey, newValue);
}

hadoop中所有基于文件的OutputFormat都是从FileOutputFormat中派生的,事实上这也是最常用的OutputFormat。总结发现,FileOutputFormat实现的主要功能有两点:

  1. 为防止用户配置的输出目录数据被意外覆盖,实现checkOutputSpecs接口,在输出目录存在时抛出异常;
  2. 处理side-effect file。hadoop可能会在一个作业执行过程中加入一些推测式任务,因此,hadoop中reduce端执行的任务并不会真正写入到输出目录,而是会为每一个Task的数据建立一个side-effect file,将产生的数据临时写入该文件,待Task完成后,再移动到最终输出目录。

默认情况下,当作业成功完成后,会在最终结果目录下生成空文件_SUCCESS,该文件主要为高层应用提供作业运行完成的标识(比如oozie工作流就可以根据这个判断任务是否执行成功)。

3. Mapper和Reducer

Mapper的过程主要包括初始化、Map操作执行和清理三个部分。Reducer过程与Mapper过程基本类似。

  1. 初始化,Mapper中的configure方法允许通过JobConf参数对Mapper进行初始化工作;
  2. Map操作,通过前面介绍的InputFormat中的RecordReader从InputSplit获取一个key/value对,交给实际的map函数进行处理;
  3. 通过继承Closable接口,获得close方法,实现对Mapper的清理。

对于一个MapReduce应用,不一定非要存在Mapper,MapReduce框架提供了比Mapper更加通用的接口:org.apache.hadoop.mapred.MapRunnable,可以直接实现该接口定制自己的key/value处理逻辑(相对于MapReduce阶段中固定的map阶段,可以跳过Map阶段,比如Hadoop Pipes中的将数据发送给其他进程处理)。
MapRunner是其固定实现,直接调用用户job中设置的Mapper Class,此外,hadoop中还提供了一个多线程的MapRunnable实现,用于非CPU类型的作业提供吞吐率。

4. Partitioner

Partitoner的作用是对Mapper产生的中间结果进行分片,将同一分组的数据交给一个Reducer来处理,直接影响这Reducer阶段的负载均衡。其中最重要的方法就是getPartition,包含三个参数,key,value,以及Reducer的个数numPartions。

MapReduce提供两个Partitioner实现,HashPartitoner和TotalOrderPartitioner。HashPartitioner是默认实现,基于哈希值进行分片;TotalOrderPartitoner提供了一种基于区间分片的方法,通常用在数据的全排序中。例如归并排序,如果Map Task进行局部排序后Reducer端进行全局排序,那么Reducer端只能设置成1个,这会成为性能瓶颈,为了提高全局排序的性能和扩展性,并保证一个区间中的所有数据都大于前一个区间的数据,就会用到TotalOrderPartitioner。

MapReduce编程模型简介和总结的更多相关文章

  1. mapreduce编程模型你知道多少?

    上次新霸哥给大家介绍了一些hadoop的相关知识,发现大家对hadoop有了一定的了解,但是还有很多的朋友对mapreduce很模糊,下面新霸哥将带你共同学习mapreduce编程模型. mapred ...

  2. MapReduce 编程模型

    一.简单介绍 1.MapReduce 应用广泛的原因之中的一个在于它的易用性.它提供了一个因高度抽象化而变得异常简单的编程模型. 2.从MapReduce 自身的命名特点能够看出,MapReduce ...

  3. MapReduce编程模型详解(基于Windows平台Eclipse)

    本文基于Windows平台Eclipse,以使用MapReduce编程模型统计文本文件中相同单词的个数来详述了整个编程流程及需要注意的地方.不当之处还请留言指出. 前期准备 hadoop集群的搭建 编 ...

  4. [转]Hadoop集群_WordCount运行详解--MapReduce编程模型

    Hadoop集群_WordCount运行详解--MapReduce编程模型 下面这篇文章写得非常好,有利于初学mapreduce的入门 http://www.nosqldb.cn/1369099810 ...

  5. MapReduce 编程模型概述

    MapReduce 编程模型给出了其分布式编程方法,共分 5 个步骤:1) 迭代(iteration).遍历输入数据, 并将之解析成 key/value 对.2) 将输入 key/value 对映射( ...

  6. MapReduce编程模型及其在Hadoop上的实现

    转自:https://www.zybuluo.com/frank-shaw/note/206604 MapReduce基本过程 关于MapReduce中数据流的传输过程,下图是一个经典演示:  关于上 ...

  7. 批处理引擎MapReduce编程模型

    批处理引擎MapReduce编程模型 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. MapReduce是一个经典的分布式批处理计算引擎,被广泛应用于搜索引擎索引构建,大规模数据处理 ...

  8. MapReduce 编程模型 & WordCount 示例

    学习大数据接触到的第一个编程思想 MapReduce.   前言 之前在学习大数据的时候,很多东西很零散的做了一些笔记,但是都没有好好去整理它们,这篇文章也是对之前的笔记的整理,或者叫输出吧.一来是加 ...

  9. 【MapReduce】二、MapReduce编程模型

      通过前面的实例,可以基本了解MapReduce对于少量输入数据是如何工作的,但是MapReduce主要用于面向大规模数据集的并行计算.所以,还需要重点了解MapReduce的并行编程模型和运行机制 ...

随机推荐

  1. python字典转化成json格式。JSONEncoder和JSONDecoder两个类来实现Json字符串和dict类型数据的互相转换

    遇到问题:进行Webservice接口测试时,对接口入参数据进行了处理,变成了dict格式,去进行接口请求报错. 需要转成成json格式,双引号去扩. 如下: 更改代码: # 在Python标准库的j ...

  2. MySQL执行计划复习

    MySQL执行计划分析 Ⅰ.认识执行计划的每个字段 (root@localhost) [(none)]> desc select 1; +----+-------------+-------+- ...

  3. Operating Systems (COMP2006)

    Operating Systems (COMP2006) 1st Semester 2019Page 1, CRICOS Number: 00301JOperating Systems (COMP20 ...

  4. Redis在windows下安装与配置

    一.安装Redis 1. Redis官网下载地址:http://redis.io/download,下载相应版本的Redis,在运行中输入cmd,然后把目录指向解压的Redis目录. 2.启动服务命令 ...

  5. Pycharm 自定义文件模板

    Pycharm 自定义文件模板 每次新建文件都有相同的代码框架,每次重复敲浪费了程序员的寿命啊 按照下面方式自定义自己的模板:

  6. 解读socketserver源码

    解读python中SocketServer源码 再看继承 真正的大餐来之前,还是来点儿开胃菜!回顾一下关于类的继承的知识:    我们先看上面的代码,这是一个简单的类继承,我们可以看到父类Base和子 ...

  7. 搭建docker私有仓库

    保存镜像的地方成为仓库(registry).目前有2种仓库:公共仓库和私有仓库. 最方便的是使用公共仓库上传和下载镜像,下载不需要注册,上传需要到公共仓库注册.公共仓库网站:https://hub.d ...

  8. nvidia-smi 实时查看

    需要用到 watch 命令: watch -n nvidia-smi

  9. MySQL学习——标识符语法和命名规则

    转自:http://blog.csdn.net/notbaron/article/details/50868485 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron ...

  10. Spring Boot:快速入门

    上一篇讲述什么是Spring Boot,这一篇讲解怎么使用IDE工具快速搭建起来独立项目. 一.构建方式 快速搭建项目有三种方式,官方也有答案给到我们: 二.构建前准备 想要使用IDE运行起来自己的S ...