MapReduce架构

来源:互联网 发布:网络it需要什么学历 编辑:程序博客网 时间:2024/03/28 21:57

MapReduce定义

源自 Google的MapReduce论文

发表于2004年12月
Hadoop MapReduce是Google MapReduce的克隆版

MapReduce特点

易于编程
良好的扩展性
高容错性
适合PB级以上海量数据的离线处理

MapReduce编程模型

一种分布式计算模型框架,解决海量数据的计算问题

MapReduce将整个并行计算过程抽象到两个函数
——>Map(映射):对一些独立元素组成的列表的每一个元素进行指定的操作,可以高度并行。
——>Reduce(化简):对一个列表的元素进行合并。

一个简单的MapReduce程序只需要指定map()、reduce()、input和output,剩下的事由架构完成。
input()——>map()——>reduce()——>output()

这里写图片描述

MapReduce系统架构

这里写图片描述

MapReduce——相关概念

JobTracker负责接收用户提交的作业,负责启动、跟踪任务执行

TaskTracker负责执行由JobTracker分配的任务,管理各个任务在每个节点上的执行情况。

Job,用户的每一个计算请求,称为一个作业。

Task,每一个作业,都需要拆分开了,交由多个服务器来完成,拆分出来的执行单位,就称为任务。

Task分为MapTask和ReduceTask两种,分别进行Map操作和Reduce操作,依据Job设置的Map类和Reduce类

MapReduce——分布式计算框架

这里写图片描述
这里写图片描述

JobTracker与TaskTracker交互

当客户端调用JobTracker来启动一个数据处理作业时,JobTracker会将工作切分,并分配不同的map和reduce任务到集群中的每个TaskTracker上。

这里写图片描述

MapReduce实例——WordCount

问题:

有一批文件(规模为TB级或者PB级),如何统计这些文件中所有单词出现的次数。

方案:

首先,分别统计每个文件中单词出现的次数。

然后,累加不同文件中同一个单词出现的次数。

MapReduce WordCount实例运行

在dfs中创建input目录

[hadoop1@H01 data]$ hadoop fs -mkdir /wc/input

将data中的.data文件拷贝到dfs中的input

[hadoop1@H01 data]$ hadoop fs -put ./*.data /wc/input

查看

[hadoop1@H01 data]$ hadoop fs -ls /wc/inputFound 3 items-rw-r--r--   1 hadoop1 supergroup         26 2015-10-24 23:42 /wc/input/00.data-rw-r--r--   1 hadoop1 supergroup         15 2015-10-24 23:42 /wc/input/1.data-rw-r--r--   1 hadoop1 supergroup        523 2015-10-24 23:42 /wc/input/worldcount.data

运行wordcount

[root@H01 hadoop-1.2.1]# hadoop jar hadoop-examples-1.2.1.jar wordcount /wc/input /wc/output

这里写图片描述

MapReduce基本流程

首先,将数据输入到HDFS,再交给map,map对每个单词进行统计

在map之后reduce之前进行排序

然后,将排好序的数据拷贝并进行合并,合并好的数据交给reduce,reduce再将完成的数据输出回HDFS

这里写图片描述

这里写图片描述

MapReduce执行流程

Map任务处理

1,读取输入文件内容,解析成key、value对。对输入文件的每一行,解析成key、value对。每一个键值对调用一次map函数。

2,写自己的逻辑,对输入的key、value处理,转换成新的key、value输出。

3,对输出的key、value进行分区

4、对不同分区的数据,按照key进行排序、分组。相同key的value放到一个集合中。

5、(可选)分组后的数据进行归约。

Reduce任务处理

1,对多个map任务的输出,按照不同的分区,通过网络copy到不同的reduce节点。

2,对多个map任务的输出进行合并、排序。写reduce函数自己的逻辑,对输入的key、value处理,转换成新的key、value输出。

3、把reduce的输出保存到文件中。

编写MapReduce程序

基于MapReduce计算模型编写分布式并行程序非常简单,程序员的主要编码工作就是实现map和reduce函数。
其他的并行编程中的种种复杂问题,如分布式存储,工作调度,负载平衡,容错处理,网络通信等,均有MapReduce框架负责处理。(JobTracker TaskTracker)

MapReduce八股文

MapReduce中,map和reduce函数遵循如下常规格式:

map:(K1,V1)——>list(K2,V2)
reduce:(K2,list(V2)) ——>list(K3,V3)

Mapper的接口:

protected void reduce(KEY key,Iterable<VALUE>values,Context context) throws IOException,interruptedException {}

Reduce的接口:

protected void reduce(KEY key,Iterable<VALUE>values,Context context) throws IOException,interruptedException {}

Context是上下文对象

实现WordCount程序编写

使用Eclipse编写MapReduce程序,打包成jar,上传到集群中,使用hadoop jar命令运行

使用MapReduce Eclipse插件程序开发并行Job

Jar包
commons-cli-1.2.jar 、commons-logging-1.1.1.jar 、hadoop-core-1.2.1.jar

这里写图片描述

MyWordCount程序

package hadoop.mr;import java.io.IOException;import java.util.StringTokenizer;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.output.FileOutputFormat;/** * MapReduce 初级案例,WordCount程序 *  */public class MyWordCount {    // Mapper 区域    /**     * KEYIN         VALUEIN         KEYOUT          VALUEOUT     *   |              |               |               |     * 输入key的类型 输入value的类型  输出key类型     输出value类型     *      *      */ static class MyMapper extends Mapper<LongWritable, Text, Text, IntWritable> {        private final static IntWritable one = new IntWritable(1);        private Text word = new Text();        @Override        protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context)                throws IOException, InterruptedException {            // 获取每行数据的值            String lineValue = value.toString();            // 进行分割            StringTokenizer st = new StringTokenizer(lineValue);            // 遍历            while (st.hasMoreTokens()) {                // 获取每一个值                String wordValue = st.nextToken();                // 设置Map输出的key值                word.set(wordValue);                // 上下文输出map的key和value                context.write(word, one);            }        }    }    // Reducer 区域    /**     * KEYIN, VALUEIN, KEYOUT, VALUEOUT wordcount中Map的输出类型就是Reduce的输入类型     * wordcount中Reduce的输出类型与他的输入类型一致     */    static class MyReduce extends Reducer<Text, IntWritable, Text, IntWritable> {        private IntWritable result = new IntWritable();        @Override        protected void reduce(Text key, Iterable<IntWritable> values,                Reducer<Text, IntWritable, Text, IntWritable>.Context context)                        throws IOException, InterruptedException {            // 用于累加的变量            int sum = 0;            // 循环遍历Iterable            for (IntWritable value : values) {                // 累加                sum += value.get();            }            // 设置总次数            result.set(sum);            context.write(key, result);        }    }    // Client 区域    public static void main(String[] args) throws Exception {        // 获取配置信息        Configuration conf = new Configuration();        // 创建job,设置配置信息与Job名称        Job job = new Job(conf, "wc");        // 1:设置Job运行的类        job.setJarByClass(MyWordCount.class);        // 2:设置Mapper和Reducer类        job.setMapperClass(MyMapper.class);        job.setReducerClass(MyReduce.class);        // 3:设置输入文件的目录和输出文件的目录        FileInputFormat.addInputPath(job, new Path(args[0]));        FileOutputFormat.setOutputPath(job, new Path(args[1]));        // 4:设置输出key,value的类型        job.setOutputKeyClass(Text.class);        job.setOutputValueClass(IntWritable.class);        // 5:提交Job等待运行结果,并在客户端显示运行信息        boolean isSuccess = job.waitForCompletion(true);        // 结束这个程序        System.exit(isSuccess ? 0 : 1);    }}

运行MyWordCount

程序编写完成后。打成jar包(wc.jar),并将包传到H01的data目录中。

注意:jar包导入的时候不可执行,要赋予执行权限:

[hadoop1@H01 data]$ chmod -R 755 wc.jar

这里写图片描述

在虚拟机下运行。

[root@H01 data]# hadoop jar wc.jar /wc/input /wc/output

警告

15/10/26 15:30:48 WARN mapred.JobClient: Use GenericOptionsParser for parsing the arguments. Applications should implement Tool for the same.

为了简化命令行方式运行作业,Hadoop自带了一些辅助类,GenericOptionsParser 是一个类,用来解释常用的Hadoop命令行选项,并根据需要,为Configuration 对象设置相应的取值。通常不直接使用GenericOptionsParser,更方便的方式是:实现Tool接口,通过ToolRunner来运行应用程序,ToolRunner内部调用GenericOptionsParser:

public interface Tool extends Configurable {    int run(String[] args) throws Exception;}

所以,要在程序中插入代码:

String[] otherArgs = new GenericOptionsParser(conf,args).getRemainingArgs();        if(otherArgs.length != 2){            System.err.println("Usage: wordcount <in> <out>");            System.exit(2);        }

完成后重新打包:

这里写图片描述

上传到H01的data目录中:

这里写图片描述
赋予执行权限:

[hadoop1@H01 data]$ chmod -R 755 wc2.jar

第二次执行:

这里写图片描述

问题解决!

安装Hadoop Eclipse插件

将hadoop-eclipse-plugin-1.2.1.jar放到eclipse安装目录的plugins文件夹中,重启eclipse。

打开Window——>Preferences,发现Hadoop Map/Reduce选项,说明插件安装成功,配置Hadoop installation directory为本地Hadoop安装解压的目录。

选择window——>open perspective——>Other…,选择有大象图标的Map/Reduce,打开Map/Reduce的开发环境,右下角多了一个Map/Reduce Locations的框。

实际操作:

links安装方法:(强推)

1,准备工作

如果插件是一个单独的jar文件,那么你需要做三件事,

先建一个plugins文件夹,把jar文件放入其中,

再建一个eclipse文件夹,把plugins放入其中,

最后,打个比方:如果你下载的是一个名为WBPro的插件,你需要建一个WBPro文件夹

,把刚才的eclipse文件夹放入其中。(不一定非得命名为WBPro,你可以用其他名字,

如AAA,bb等,注:此句话可先不看)。好了,插件的大致结构出来了,如下

WBPro/eclipse /plugins/**.jar

如果插件是plugins和features两个文件夹(或只有plugins文件夹),

请建一个名为eclipse的文件夹,把上面的两个文件夹放入其中,

再建一个名为WBPro的文件夹,把刚才的eclipse文件夹放入其中,插件的大致结构如

下:WBPro/eclipse /{plugins,features}如果插件就是一个eclipse文件夹,请新建

一个WBPro文件夹,把eclipse放入其中,插件的结构如下:WBPro/eclipse

2,开始安装

找到你的eclipse ,在其下建两个文件夹,一个是extplugins,一个是links,结构如

下{eclipse/extplugins,links},

把你刚才建立的插件WBPro放入extplugins中,

然后在links文件夹下,建立一个txt文本,在里面写入:path=extplugins/WBPro,注

意是“/”,而不是“\”,再把文本文件命名为WBPro.link,注意全名是WBPro.link,

而不是WBPro.link.txt,

好了,启动eclipse,在菜单栏点击window->Preferences,在弹出来的对话框中,看左

边的导航栏,如果里面有你的插件名,如WBPro,恭喜你插件安装成功!

这里写图片描述

这里写图片描述

WordCount处理过程

将文件拆分成splits,由于测试用的文件较小,所以每个文件为一个splits,并将文件按行分割形成键值对。下图所示。这一步由MapReduce框架自动完成。其中偏移量(即key值)包括了回车所占的字符数(Windows/Linux环境不同)。
这里写图片描述

将分割好的键值对交给用户定义的map方法进行处理,生成新的键值对,下图所示。
这里写图片描述

得到map方法输出的键值对后,Mapper会将他们按照key值进行排序,并执行Combine过程,将key值相同value值累加,得到Mapper的最终输出结果。下图所示。
这里写图片描述

Reducer先对Mapper接收的数据进行排序,再交由用户自定义的reduce方法进行处理,得到新的键值对,并作为WordCount的输出结果,下图所示。
这里写图片描述

MapReduce工作原理

这里写图片描述

MR作业运行流程分析

1、在客户端启动一个作业;

2、向JobTracker请求一个Job ID;

3、将运行作业所需要的资源文件复制到HDFS上,包括MapReduce程序打包的JAR文件、配置文件和客户端计算所得的输入划分信息。这些文件都存放在JobTracker专门为该作业创建的文件夹中。文件夹名为该作业的Job ID。JAR文件默认会有10个副本(mapred.submit.replication属性控制);输入划分信息告诉了JobTracker应该为这个作业启动多少个map任务等信息;

4、JobTracker接收作业后,将其放在一个作业队列里,等待作业调度器对其进行调度(这里是不是很像微机中的进程调度呢?)。当作业调度器根据自己的调度算法调度到该作业时,会根据输入划分信息为每个划分创建一个map任务。并将map任务分配给TaskTracker执行。对于map和reduce任务,TaskTracker根据主机核的数量和内存的大小有固定数量的map槽和reduce槽,这里需要强调的是:map任务不是随随便便的分配给某个TaskTracker的,这里有个概念叫:数据本地化(Data-Local)。意思是:将map任务分配给含有该TaskTracker上来运行,这叫“运算移动,数据不移动”。而分配reduce任务时并不考虑数据本地化。

5、TaskTracker每隔一段时间会给JobTracker发送一个心跳,告诉JobTracker它依然在运行。同时心跳中还携带着很多的信息,比如当前map任务完成的进度等信息。当JobTracker收到作业的最后一个任务完成信息时,便把该作业设置成“成功”。当JobClient查询状态时,它将得知任务已完成,便显示一条消息给用户。

Map端流程分析

1,每个输入分片会让一个map任务来处理,默认情况下,以HDFS的一个块的大小(默认为64MB)为一个分片,当然我们也可以设置块的大小。Map输出的结果会暂时放在一个环形内存缓冲区中(该缓冲区的大小默认为100MB,由io.sort.mb属性控制),当该缓冲区快要溢出时(默认为缓冲区大小的80%,由io.sort.spill.percent属性控制),会在本地文件系统中创建一个溢出文件,将该缓冲区中的文件写入其中。

2,在写入磁盘之前,线程首先根据Reduce任务的数目将数据划分为相同数目的分区,也就是一个reduce任务对应一个分区的数据。这样做是为了避免有些reduce任务分配到大量数据,而有些reduce分配到很少的数据甚至没有数据的尴尬。其实分区就是对数据进行hash的过程。然后对每个分区的数据进行排序,如果此时设置了Combiner,将排序后的结果进行Combia操作,这样做的目的是让尽可能少的数据写入到磁盘。

3,当Map任务输出最后一个任务时,可能会有很多溢出文件,这是需要将这些文件合并,合并的过程会不断的进行排序和cnmbia操作,目的有两个:1.尽量减少每次写入磁盘的数据量;2.尽量减少下一阶段网络传输的数据量。最后合并成了一个已分区且已排序的文件。为了减少网络传输的数据量,这里可以将数据压缩,只要将mapred.compress.map.out设置成true就可以了。(数据压缩:Gzip——>Lzo,snappy)

4,将分区的数据拷贝给相应的Reduce任务,但是分区中的数据怎么知道它对应的是哪个Reduce呢?其实map任务一直和其父TaskTracker保持联系,而TaskTracker又一直跟JobTracker保持心跳联系,所以JobTracker中保存了整个集群中的宏观信息,只要Reduce任务向JobTracker获取对应的map输出位置就可以了。

5,map端就分析完了,那么什么是Shuffle呢?Shuffle的中文意思是洗牌,如果我们这样看:一个map产生的数据,结果通过hash过程分区却分配了不同的Reduce任务,是不是一个对数据重新洗牌的过程呢?

Reduce端流程分析

1,Reduce端会拷贝不同的Map端的数据,并且每个来自Map的数据都是有序的,如果Reduce端接收的数据量相当小,则直接存储到内存中(缓冲区大小由mapred.job.shuffle.input.buffer.percent属性控制,
表示用作此用途的堆空间的百分比),如果数据量超过了该缓冲区大小的一定比例(由mapred.job.shuffle.merge.percent决定),则对数据合并后溢写到磁盘中。

2,随着溢出文件的增多,后台线程会将他们合并成一个更大的有序的文件,这样做是为了给后台的合并节省时间。其实不管在map端还是reduce端,MapReduce都是反复的执行排序,合并操作。

3,合并的过程中会产生许多的中间文件(写入磁盘了),但MapReduce会让写入磁盘的数据尽可能的少,并且最后一次合并的结果并没有写入磁盘,而是直接输入到Reduce函数。

MapReduce之Shuffle过程

Shuffle概念

意思:洗牌或弄乱
Collections.shuffle(List):随机的打乱参数list里的元素顺序。
MapReduce里的Shuffle:描述着数据从map task输出到reduce task输入的这段过程。

这里写图片描述
这里写图片描述

Map Shuffle Phase

这里写图片描述
1,输入——>2,分区——>3,溢写(排序,相同键的值对合并)——>4,文件合并

每个map task都有一个内存缓冲区,存储着map的输出结果,当缓冲区快满的时候需要将缓冲区的数据以一个临时文件的方式存放到磁盘。当整个map task结束后在对这个map task产生的所有临时文件做合并,生成最终的正式输出文件,然后等待reduce task来拉数据。

1, 在map task执行时,他的输入数据来源于HDFS的block,当然在MapReduce概念中,map task只读取split。Split与block的对应关系可能是多对一,默认是一对一。在WordCount例子中,假设map的输入数据都是像“aaa”这样的字符串。

2,在经过mapper的运行后,我们得知mapper的输出是这样一个key/value对:key是“aaa”,value是数值1。因为当前map端只做加1的操作,在reduce task里才去合并结果集。前面我们知道这个job有3个reduce task,到底当前的“aaa”应该交由哪个reduce去做呢?是需要现在决定的。

MapReduce提供Partitioner接口,它的作用就是根据key或value及reduce的数量来决定当前的这对输出数据最终应该交由哪个reduce task处理。默认对key hash后再以reduce task数量取模。默认的取模方式只是为了平均reduce的处理能力,如果用户自己对Partitioner有需求,可以订制并设置到job上。

在我们的例子中,“aaa”经过Partitioner后返回0,也就是这对值应当交由第一个reducer来处理。接下来,需要将数据写入内存缓冲区中(也就是说,先进行分区,再写入缓冲区),缓冲区的作用是批量收集map结果,减少磁盘IO的影响。我们的key/value对以及Partition的结果都会被写入缓冲区。当然写入之前,key与value值都会被序列化成字节数组。

疑问: 在我们的例子中,“aaa”经过Partitioner后返回0,也就是这对值应当交由第一个reducer来处理。接下来,需要将数据写入内存缓冲区中。。。。。。这段话的意思是shuffle过程中map处理过的数据要先进行分区再写入缓冲区么?可是跟图片上的过程不符合啊?

溢写spill

3,内存缓冲区是有大小限制的,默认是100MB。当map task的输出结果很多时,就可能撑爆内存,所以需要在一定条件下将缓冲区的数据临时写入磁盘,然后重新利用这块缓冲区。这个从内存往磁盘写数据的过程被称为Spill,中文可译为溢写。溢写是由单独线程完成的,不影响往缓冲区写map结果的线程,溢写线程启动时不应该阻止map的结果输出,所以整个缓冲区有个溢写的比例spill.percent。比例默认是0.8,也就是当缓冲区的数据已经达到阀值(buffer size*spill percent = 100MB*0.8 = 80MB),溢写线程启动,锁定这80MB的内存,执行溢写过程。Map task的输出结果还可以往剩下的20MB内存中写,互不影响。

当溢写线程启动后,需要对这80MB的空间内的key做排序(sort)。排序是MapReduce模型默认的行为,这里的排序也是对序列化的字节做的排序。

因为map task的输出是要发送到不同的reduce端去,而内存缓冲区没有对将发送到相同reduce端的数据做合并,那么这种合并应该是体现在磁盘文件中的。从官方图上也可以看到写到磁盘中的溢写文件是对不同的reduce端的数值做过合并。所以溢写过程一个很重要的细节在于,如果有很多个key/value对需要发送到某个reduce端去,那么需要将这些key/value值拼接到一起,减少与partition相关的索引记录。

在针对每个reduce端而合并数据时,有些数据可能像这样:“aaa”/1,“aaa”/1。对于WordCount例子,就是简单的统计单词出现的次数,如果在同一个map task的结果中有很多个像“aaa”一样出现多次的key,我们就应该把它们的值合并到一块,这个过程叫reduce也叫combine。但MapReduce的术语中,reduce只指reduce端执行从多个map task取数据做计算的过程。除reduce外,非正式的合并数据只能算作combine了。其实大家知道的,MapReduce中将Combiner等同于Reducer。

如果client设置过Combiner,那么现在就是使用Combiner的时候了。将有相同key的key/value对的value加起来,减少溢写到磁盘的数据量。Combiner会优化MapReduce的中间结果,所以它在整个模型中会多次使用。那哪些场景才能使用Combiner呢?从这里分析,Combiner的输出是Reducer的输入,Combiner绝不能改变最终的计算结果。所以从我的想法来看,Combiner只应该用于那种Reduce的输入key/value与输出key/value类型完全一致,且不影响最终结果的场景。比如累加,最大值等。Combiner的使用一定得慎重,如果用好,它对job执行效率有帮助,反之会影响reduce的最终结果。

4, 每次溢写会在磁盘上生成一个溢写文件,如果map的输出结果真的很大,有多次这样的溢写发生,磁盘上相应的就会有多个溢写文件存在。当map task真正完成时,内存缓冲区中的数据也全部溢写到磁盘中形成一个溢写文件。最终磁盘中会至少有一个这样的溢写文件存在(如果map的输出结果很少,当map执行完成时,只会产生一个溢写文件),因为最终的文件只有一个,所以需要将这些溢写文件归并到一起,这个过程就叫做Merge。

Merge是怎样的?如前面的例子,“aaa”从某个map task读取过来时值是5,从另外一个map 读取时值是8,因为它们有相同的key,所以得merge成group。

这里写图片描述

什么是group。对于“aaa”就是像这样的:{“aaa”, [5, 8, 2, …]},数组中的值就是从不同溢写文件中读取出来的,然后再把这些值加起来。请注意,因为merge是将多个溢写文件合并到一个文件,所以可能也有相同的key存在,在这个过程中如果client设置过Combiner,也会使用Combiner来合并相同的key。

至此,map端的所有工作都已结束,最终生成的这个文件也存放在TaskTracker够得着的某个本地目录内。每个reduce task不断地通过RPC从JobTracker那里获取map task是否完成的信息,如果reduce task得到通知,获知某台TaskTracker上的map task执行完成,Shuffle的后半段过程开始启动。

环形缓冲区

这里写图片描述

  1. kvoffsets即偏移量索引数组,用于保存key/value在kvindices中的偏移量。一个key/value对在kvoffsets数组中占一个int的大小,而在kvindices数组中站3个int的大小(如上图示,包括分区号partition,key的起始位置和value的起始位置)。当kvoffsets的使用率超过io.sort.spill.percent后,便会触发SpillTread线程将数据spill到磁盘上。

  2. kvindices即文职索引数组,用于保存实际的key/value在数据缓冲区kvbuffer中的起始位置。

  3. kvbuffer即数据局缓冲区,用于实际保存key/value,默认情况下可使用io.sort.mb的95%,当该缓冲区使用率使用率超过io.sort.spill.percent(默认80%)后,便会触发SpillTread线程将数据spill到磁盘上。

Reduce Shuffle Phase

这里写图片描述

  1. Copy过程,简单地拉取数据。Reduce进程启动一些数据copy线程(Fetcher),通过HTTP方式请求map task所在的TaskTracker获取map task的输出文件。因为map task早已结束,这些文件就归TaskTracker管理在本地磁盘中。

  2. Merge阶段。这里的merge如map端的merge动作,只是数组中存放的是不同map端copy来的数值。Copy过来的数据会先放入内存缓冲区中,这里的缓冲区大小要比map端的更为灵活,它基于JVM的heap size设置,因为Shuffle阶段Reducer不运行,所以应该把绝大部分的内存都给Shuffle用。这里需要强调的是,merge有三种形式:1)内存到内存 2)内存到磁盘 3)磁盘到磁盘。默认情况下第一种形式不启用,让人比较困惑,是吧。当内存中的数据量到达一定阈值,就启动内存到磁盘的merge。与map 端类似,这也是溢写的过程,这个过程中如果你设置有Combiner,也是会启用的,然后在磁盘中生成了众多的溢写文件。第二种merge方式一直在运行,直到没有map端的数据时才结束,然后启动第三种磁盘到磁盘的merge方式生成最终的那个文件。

  3. Reducer的输入文件。不断地merge后,最后会生成一个“最终文件”。为什么加引号?因为这个文件可能存在于磁盘上,也可能存在于内存中。对我们来说,当然希望它存放于内存中,直接作为Reducer的输入,但默认情况下,这个文件是存放于磁盘中的。当Reducer的输入文件已定,整个Shuffle才最终结束。然后就是Reducer执行,把结果放到HDFS上。

对MapReduce的调优在很大程度上就是对MapReduce Shuffle的性能的调优。

MapReduce作业中Map Task数目的确定

1)MapReduce从HDFS中分割读取Split文件,通过Inputformat交给Mapper来处理。Split 是 MapReduce 中最小的计算单元,一个 Split 文件对应一个Map Task。

2)默认情况下HDFS中的一个block,对应一个 Split。

3)当执行 wordcount 时:
->(1)一个输入文件如果小于64MB,默认情况则保存在hdfs上的一个block中,对应一个Split文件,所以将产生一个Map Task

->(2)如果输入一个文件为150MB,默认情况则保存在hdfs上的三个block中,对应三个Split文件,所以将产生三个Map Task

->(3)如果有输入三个文件都小于64MB,默认情况下会保存在三个不同的block中,所以对应三个Split文件,也将产生三个Map Task

4)用户可自行指定block与Split之间的关系,HDFS中的一个Split对应一个block,一个Split也可以对应多个block,Split与block是一对多的关系

5)总结MapReduce作业中的Map Task数目是由:
->(1)输入文件的大小与个数

->(2)hadoop设置split与block的关系

MapReduce作业中Reduce Task数目的确定:

1)JobClient类中submitJobInternal方法中指定 int reduces = jobCopy.getNumReduceTask();

2)而JobConf类中,

public int getNumReduceTask(){  return getInt("mapred.reduce.tasks",1);}

因此,Reduce Task数目是由mapred.reduce.tasks,如果不指定默认为1。

0 0
原创粉丝点击