分析MapReduce执行过程

MapReduce运行的时候,会通过Mapper运行的任务读取HDFS中的数据文件,然后调用自己的方法,处理数据,最后输出。 Reducer任务会接收Mapper任务输出的数据,作为自己的输入数据,调用自己的方法,最后输出到HDFS的文件中。

Mapper任务的执行过程

每个Mapper任务是一个java进程,它会读取HDFS中的文件,解析成很多的键值对,经过我们覆盖的map方法处理后,转换为很多的键值对再输出。整个Mapper任务的处理过程又可以分为以下几个阶段。

把Mapper任务的运行过程分为六个阶段。

  • 第一阶段是把输入文件按照一定的标准分片(InputSplit),每个输入片的大小是固定的。默认情况下,输入片(InputSplit)的大 小与数据块(Block)的大小是相同的。如果数据块(Block)的大小是默认值64MB,输入文件有两个,一个是32MB,一个是72MB。那么小的 文件是一个输入片,大文件会分为两个数据块,那么是两个输入片。一共产生三个输入片。每一个输入片由一个Mapper进程处理。这里的三个输入片,会有三个Mapper进程处理。
  • 第二阶段是对输入片中的记录按照一定的规则解析成键值对。有个默认规则是把每一行文本内容解析成键值对。“键”是每一行的起始位置(单位是字节),“值”是本行的文本内容。
  • 第三阶段是调用Mapper类中的map方法。第二阶段中解析出来的每一个键值对,调用一次map方法。如果有1000个键值对,就会调用1000次map方法。每一次调用map方法会输出零个或者多个键值对。
  • 第四阶段是按照一定的规则对第三阶段输出的键值对进行分区。比较是基于键进行的。比如我们的键表示省份(如北京、上海、山东等),那么就可以按照不同省份进行分区,同一个省份的键值对划分到一个区中。默认是只有一个区分区的数量就是Reducer任务运行的数量。默认只有一个Reducer任务。
  • 第五阶段是对每个分区中的键值对进行排序。首先,按照键进行排序,对于键相同的键值对,按照值进行排序。比如三个键值 对<2,2>、<1,3>、<2,1>,键和值分别是整数。那么排序后的结果 是<1,3>、<2,1>、<2,2>。如果有第六阶段,那么进入第六阶段;如果没有,直接输出到本地的linux 文件中。
  • 第六阶段是对数据进行归约处理,也就是reduce处理。键相等的键值对会调用一次reduce方法。经过这一阶段,数据量会减少。归约后的数据输出到本地的linxu文件中。本阶段默认是没有的,需要用户自己增加这一阶段的代码

Reducer任务的执行过程

每个Reducer任务是一个java进程。Reducer任务接收Mapper任务的输出,归约处理后写入到HDFS中,可以分为以下几个阶段。

  • 第一阶段是Reducer任务会主动从Mapper任务复制其输出的键值对。Mapper任务可能会有很多,因此Reducer会复制多个Mapper的输出。
  • 第二阶段是把复制到Reducer本地数据,全部进行合并,即把分散的数据合并成一个大的数据。再对合并后的数据排序。
  • 第三阶段是对排序后的键值对调用reduce方法。键相等的键值对调用一次reduce方法,每次调用会产生零个或者多个键值对。最后把这些输出的键值对写入到HDFS文件中。

在整个MapReduce程序的开发过程中,我们最大的工作量是覆盖map函数和覆盖reduce函数。

键值对的编号

在对Mapper任务、Reducer任务的分析过程中,会看到很多阶段都出现了键值对,读者容易混淆,所以这里对键值对进行编号,方便大家理解键值对的变化情况

对于Mapper任务输入的键值对,定义为key1和value1。在map方法中处理后,输出的键值对,定义为key2和 value2。reduce方法接收key2和value2,处理后,输出key3和value3。在下文讨论键值对时,可能把key1和value1简 写为<k1,v1>,key2和value2简写为<k2,v2>,key3和value3简写为<k3,v3>。

举例:单词计数

该业务要求统计指定文件中的所有单词的出现次数。

内容很简单,两行文本,每行的单词中间使用空格区分。

分析思路:最直观的想法是使用数据结构Map。解析文件中出现的每个单词,用单词作为key,出现次数作为value。这个思路没有问题,但是在大 数据环境下就不行了。我们需要使用MapReduce来做。根据Mapper任务和Reducer任务的运行阶段,我们知道在Mapper任务的第二阶段 是把文件的每一行转化成键值对,那么第三阶段的map方法就能取得每一行文本内容,我们可以在map方法统计本行文本中单词出现的次数,把每个单词的出现 次数作为新的键值对输出。在Reducer任务的第二阶段会对Mapper任务输出的键值对按照键进行排序,键相等的键值对会调用一次reduce方法。 在这里,“键”就是单词,“值”就是出现次数。因此可以在reduce方法中对单词的不同行中的所有出现次数相加,结果就是该单词的总的出现次数。最后把 这个结果输出。

看一下如何覆盖map方法

static class MyMapper extends Mapper<LongWritable, Text, Text, IntWritable>{

//key2 表示该行中的单词

final Text key2 = new Text();

//value2 表示单词在该行中的出现次数

final IntWritable value2 = new IntWritable(1);

//key 表示文本行的起始位置,也即是偏移量

//value 表示文本行

protected void map(LongWritable key, Text value, Context context) throws java.io.IOException ,InterruptedException {

final String[] splited = value.toString().split(" ");

for (String word : splited) {

key2.set(word);

//把key2、value2写入到context中

context.write(key2, value2);

}

};

}

上面代码中,注意Mapper类的泛型不是java的基本类型,而是Hadoop的数据类型LongWritable、Text、IntWritable。读者可以简单的等价为java的类long、String、int。下文会有专门讲解Hadoop的数据类型。

代码中Mapper类的泛型依次是<k1,v1,k2,v2>。map方法的第二个形参是行文本内容,是我们关心的。核心代码是把行文 本内容按照空格拆分,把每个单词作为新的键,数值1作为新的值,写入到上下文context中。在这里,因为输出的是每个单词,所以出现次数是常量1。如果一行文本中包括两个hello,会输出两次<hello,1>。

再来看一下如何覆盖reduce方法

static class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable>{

//value3表示单词出现的总次数

final IntWritable value3 = new IntWritable(0);

/**

* key 表示单词

* values 表示map方法输出的1的集合

* context 上下文对象

*/

protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws Exception {

int sum = 0;

for (IntWritable count : values) {

sum += count.get();

}

//执行到这里,sum表示该单词出现的总次数

//key3表示单词,是最后输出的key

final Text key3 = key;

//value3表示单词出现的总次数,是最后输出的value

value3.set(sum);

context.write(key3, value3);

};

}

上面代码中,Reducer类的四个泛型依次是<k2,v2,k3,v3>,要注意reduce方法的第二个参数是java.lang.Iterable类型,迭代的是v2。也就是k2相同的v2都可以迭代出来。

以上就是我们覆盖的map方法和reduce方法。现在要把我们的代码运行起来,需要写驱动代码,如下

/**

* 驱动代码

*/

public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {

//输入路径

final String INPUT_PATH = "hdfs://hadoop0:9000/input";

//输出路径,必须是不存在的

final String OUTPUT_PATH = "hdfs://hadoop0:9000/output";

//创建一个job对象,封装运行时需要的所有信息

final Job job = new Job(new Configuration(),"WordCountApp");

//如果需要打成jar运行,需要下面这句

job.setJarByClass(WordCountApp.class);

//告诉job执行作业时输入文件的路径

FileInputFormat.setInputPaths(job, INPUT_PATH);

//设置把输入文件处理成键值对的类

job.setInputFormatClass(TextInputFormat.class);

//设置自定义的Mapper类

job.setMapperClass(MyMapper.class);

//设置map方法输出的k2、v2的类型

job.setMapOutputKeyClass(Text.class);

job.setMapOutputValueClass(IntWritable.class);

//设置对k2分区的类

job.setPartitionerClass(HashPartitioner.class);

//设置运行的Reducer任务的数量

job.setNumReduceTasks(1);

//设置自定义的Reducer类

job.setReducerClass(MyReducer.class);

//设置reduce方法输出的k3、v3的类型

job.setOutputKeyClass(Text.class);

job.setOutputValueClass(IntWritable.class);

//告诉job执行作业时的输出路径

FileOutputFormat.setOutputPath(job, new Path(OUTPUT_PATH));

//指明输出的k3类型

job.setOutputKeyClass(Text.class);

//指明输出的v3类型

job.setOutputValueClass(IntWritable.class);

//让作业运行,直到运行结束,程序退出

job.waitForCompletion(true);

}

在以上代码中,我们创建了一个job对象,这个对象封装了我们的任务,可以提交到Hadoop独立运行。最后一句job.waitForCompletion(true),表示把job对象提交给Hadoop运行,直到作业运行结束后才可以。

以上代码的运行方式有两种,一种是在宿主机的eclipse环境中运行,一种是打成jar包在linux中运行。

第一种运行方式要求宿主机能够访问linux,并且对于输入路径和输出路径中的主机名hadoop0,要在宿主机的hosts文件中有绑定,笔者的hosts文件位于C:\WINDOWS\system32\drivers\etc文件夹。

第二种运行方式,需要把代码打成jar包,在linux下执行命令hadoop jar xxx.jar运行

运行结束后,文件路径在hdfs://hadoop0:9000/output/part-r-00000。

分析MapReduce执行过程的更多相关文章

  1. 分析MapReduce执行过程+统计单词数例子

    MapReduce 运行的时候,会通过 Mapper 运行的任务读取 HDFS 中的数据文件,然后调用自己的方法,处理数据,最后输出.Reducer 任务会接收 Mapper 任务输出的数据,作为自己 ...

  2. Hadoop MapReduce执行过程实例分析

    1.MapReduce是如何执行任务的?2.Mapper任务是怎样的一个过程?3.Reduce是如何执行任务的?4.键值对是如何编号的?5.实例,如何计算没见最高气温? 分析MapReduce执行过程 ...

  3. Hadoop MapReduce执行过程详解(带hadoop例子)

    https://my.oschina.net/itblog/blog/275294 摘要: 本文通过一个例子,详细介绍Hadoop 的 MapReduce过程. 分析MapReduce执行过程 Map ...

  4. Hadoop学习之Mapreduce执行过程详解

    一.MapReduce执行过程 MapReduce运行时,首先通过Map读取HDFS中的数据,然后经过拆分,将每个文件中的每行数据分拆成键值对,最后输出作为Reduce的输入,大体执行流程如下图所示: ...

  5. MapReduce执行过程

    Mapper任务的执行过程: 第一阶段是把输入文件按照一定的标准分片(InputSplit),每个输入片的大小是固定的.默认情况下,输入片(InputSplit)的大小与数据块(Block)的大小是相 ...

  6. Hadoop mapreduce执行过程涉及api

    资源的申请,分配过程略过,从开始执行开始. mapper阶段: 首先调用默认的PathFilter进行文件过滤,确定哪些输入文件是需要的哪些是不需要的,然后调用inputFormat的getSplit ...

  7. 深入源码分析SpringMVC执行过程

    本文主要讲解 SpringMVC 执行过程,并针对相关源码进行解析. 首先,让我们从 Spring MVC 的四大组件:前端控制器(DispatcherServlet).处理器映射器(HandlerM ...

  8. 精尽MyBatis源码分析 - SQL执行过程(二)之 StatementHandler

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  9. 精尽MyBatis源码分析 - SQL执行过程(三)之 ResultSetHandler

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

随机推荐

  1. java利用过滤器实现编码的转换,内容输出的替换

    在页面建个表单 <form action="login.do" method="post"> <input type="text&q ...

  2. types.MethodType

    http://stackoverflow.com/questions/972/adding-a-method-to-an-existing-object-instance 532down voteac ...

  3. Codeforces 519E A and B and Lecture Rooms [倍增法LCA]

    题意: 给你一棵有n个节点的树,给你m次询问,查询给两个点,问树上有多少个点到这两个点的距离是相等的.树上所有边的边权是1. 思路: 很容易想到通过记录dep和找到lca来找到两个点之间的距离,然后分 ...

  4. [HDU 4417] Super Mario (树状数组)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417 题目大意:给你n个数,下标为0到n-1,m个查询,问查询区间[l,r]之间小于等于x的数有多少个 ...

  5. nearly,about,almost的区别

    nearly 几乎,将近almost 几乎,差一点儿就,差不多(与动词,副词,形容词以及名词连用时,可与nearly通用;与no,none,nothing,never等否定式连用时,不可与nearly ...

  6. Hello world!让 grub2 引导自己的操作系统 Xos 内核

    按照惯例,Xos 的第一步是在屏幕上打印 Hello world!第一步是神奇的一步,如果读者对 PC 不了解,将很难得到头绪. PC 开机后,CS 和 IP 被初始化为 CS=0xFFFFh,IP= ...

  7. MSP430F149学习之路——比较器Comparaor_A

    代码一: #include <msp430x14x.h> ; void int_clk() { BCSCTL1 &= ~XT2OFF; BCSCTL2 |= SELM_2 + SE ...

  8. noi2010 能量采集

    2005: [Noi2010]能量采集 Time Limit: 10 Sec  Memory Limit: 552 MB Submit: 3068  Solved: 1820 [Submit][Sta ...

  9. NULL值比较,两个列的合并,列值按条件替换。

    show create table 表名 -- 显示创建表的sql语句. 为已有的表增加新列.alter table 表名 add 列名 int NULL -- 此行加了一个int 类型 默认可以nu ...

  10. Windows API学习---插入DLL和挂接API

    插入DLL和挂接API 在Microsoft Windows中,每个进程都有它自己的私有地址空间.当使用指针来引用内存时,指针的值将引用你自己进程的地址空间中的一个内存地址.你的进程不能创建一个其引用 ...