Hadoop之MapReduce介绍整理

Hadoop之MapReduce介绍整理

什么是批处理

​ 在了解MapReduce之前,需要了解批处理的概念,批处理模式是一种最早进行大规模数据处理的模式。批处理主要操作大规模静态数据集,并在整体数据处理完毕后返回结果。批处理非常适合需要访问整个数据集合才能完成的计算工作。

​ 例如,在计算总数和平均数时,必须将数据集作为一个整体加以处理,而不能将其视作多条记录的集合。这些操作要求在计算进行过程中数据维持自己的状态。

​ 需要处理大量数据的任务通常最适合用批处理模式进行处理,批处理系统在设计过程中就充分考虑了数据的量,可提供充足的处理资源。

由于批处理在应对大量持久数据方面的表现极为出色,因此经常被用于对历史数据进行分析。为了提高处理效率,对大规模数据集进行批处理需要借助分布式并行程序。

​ 传统的程序基本是以单指令、单数据流的方式按顺序执行的。这种程序开发起来比较简单,符合人们的思维习惯,但是性能会受到单台计算机的性能的限制,很难在给定的时间内完成任务。而分布式并行程序运行在大量计算机组成的集群上,可以同时利用多台计算机并发完成同一个数据处理任务,提高了处理效率,同时,可以通过增加新的计算机扩充集群的计算能力。

​ Google 最先实现了分布式并行处理模式 MapReduce,并于 2004 年以论文的方式对外公布了其工作原理,Hadoop MapReduce 是它的开源实现。Hadoop MapReduce 运行在 HDFS 上。

MapReduce 概述定义

MapReduce是一个分布式运算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架。

MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上。

举例: 如果我们想知道相当厚的一摞牌中有多少张红桃,最直观的方式就是一张张检查这些牌,并且数出有多少张是红桃,最终数完这一摞牌后得出结果;这种方法的缺陷是速度太慢,特别是在牌的数量特别高的情况下,比如有两万张牌,一张张数过去获取结果的时间会很长。

按照MapReduce 方法的规则如下。

  • 把这摞牌分配给在座的多位玩家。
  • 让每个玩家数自己手中的牌中有几张是红桃,然后把这个数目汇报上来。
  • 把所有玩家汇报的数字加起来,得到最后的结论。

显而易见,MapReduce 方法通过让所有玩家同时并行检查牌来找出一摞牌中有多少红桃,可以大大加快得到答案的速度。

MapReduce 方法使用了拆分任务的思想,合并结果两种经典函数。

1)映射(Map)

对集合中的每个元素进行同一个操作。如果想把表单里每个单元格乘以二,那么把这个函数单独地应用在每个单元格上的操作就属于映射(Map)。

上述例子中对集合中的每个元素进行同一个操作即对每张牌都在判断是否是红桃

2)化简(Reduce)

遍历集合中的元素来返回一个综合的结果。如果想找出表单里所有数字的总和,那么输出表单里一列数字的总和的任务就属于化简(Reduce)。

下面使用 MapReduce 数据分析的基本方法来重新审视前面分散纸牌找出红桃总数的例子。在这个例子里,玩家代表计算机,因为他们同时工作,所以他们是个集群。

把牌分给多个玩家并且让他们各自数数,就是在并行执行运算,此时每个玩家都在同时计数。这就把这项工作变成了分布式的,因为多个不同的人在解决同一个问题的过程中并不需要知道他们的邻居在干什么。

告诉每个人去数数,实际上就是对一项检查每张牌的任务进行了映射。不是让玩家把红桃牌递回来,而是让他们把想要的东西化简为一个数字。

需要注意的是牌分配得是否均匀。如果某个玩家分到的牌远多于其他玩家,那么他数牌的过程可能比其他人要慢很多,从而会影响整个数牌的进度。

进一步还可以问一些更有趣的问题,例如,“一摞牌的平均值是什么?”。我们可以通过合并“所有牌的值的和是什么?”及“我们有多少张牌?”这两个问题来得到答案。用这个“和”除以“牌的张数”就得到了平均值。

MapReduce 算法的机制要远比数牌复杂得多,但是主体思想是一致的,即通过分散计算来分析大量数据。无论是 Google、百度、腾讯、NASA,还是小创业公司,MapReduce 都是目前分析互联网级别数据的主流方法。

MapReduce优缺点

优点:

1.MapReduce 易于编程

它简单的实现一些接口,就可以完成一个分布式程序,这个分布式程序可以分布到大量廉价的PC机器上运行。也就是说你写一个分布式程序,跟写一个简单的串行程序是一模一样的。就是因为这个特点使得MapReduce编程变得非常流行。

2.良好的扩展性

当你的计算资源不能得到满足的时候,你可以通过简单的增加机器来扩展它的计算能力。

3.高容错性

MapReduce设计的初衷就是使程序能够部署在廉价的PC机器上,这就要求它具有很高的容错性。比如其中一台机器挂了,它可以把上面的计算任务转移到另外一个节点上运行,不至于这个任务运行失败,而且这个过程不需要人工参与,而完全是由Hadoop内部完成的。

4.适合PB级以上海量数据的离线处理

可以实现上千台服务器集群并发工作,提供数据处理能力。

缺点:

  1. 不擅长实时计算

    MapReduce无法像MySQL一样,在毫秒或者秒级内返回结果。

  2. 不擅长流式计算

    流式计算的输入数据是动态的,而MapReduce的输入数据集是静态的,不能动态变化。这是因为MapReduce自身的设计特点决定了数据源必须是静态的。

  3. 不擅长DAG(有向图)计算

    多个应用程序存在依赖关系,后一个应用程序的输入为前一个的输出。在这种情况下,MapReduce并不是不能做,而是使用后,每个MapReduce作业的输出结果都会写入到磁盘,会造成大量的磁盘IO,导致性能非常的低下。

MapReduce 思想

在这里插入图片描述

​ 使用 MapReduce 处理大数据的基本思想包括 3 个层面。首先,对大数据采取分而治之的思想。对相互间不具有计算依赖关系的大数据实现并行处理,最自然的办法就是采取分而治之的策略。

其次,把分而治之的思想上升到抽象模型。为了克服 MPI 等并行计算方法缺少高层并行编程模型这一缺陷,MapReduce 借鉴了 Lisp 函数式语言中的思想,用 Map 和 Reduce 两个函数提供了高层的并行编程抽象模型。

最后,把分而治之的思想上升到架构层面,统一架构为程序员隐藏系统层的实现细节。

MPI 等并行计算方法缺少统一的计算框架支持,程序员需要考虑数据存储、划分、分发、结果收集、错误恢复等诸多细节,为此,MapReduce 设计并提供了统一的计算框架,为程序员隐藏了绝大多数系统层面的处理细节。

1. 大数据处理思想:分而治之

并行计算的第一个重要问题是如何划分计算任务或者计算数据以便对划分的子任务或数据块同时进行计算。但是,一些计算问题的前后数据项之间存在很强的依赖关系,无法进行划分,只能串行计算。

对于不可拆分的计算任务或相互间有依赖关系的数据无法进行并行计算。一个大数据若可以分为具有同样计算过程的数据块,并且这些数据块之间不存在数据依赖关系,则提高处理速度的最好办法就是并行计算。

例如,假设有一个巨大的 2 维数据,大得无法同时放进一个计算机的内存,如图 2 所示,现在需要求每个元素的开立方。因为对每个元素的处理是相同的,并且数据元素间不存在数据依赖关系,因此可以考虑将其划分为子数组,由一组计算机并行处理。

2. 构建抽象模型:Map 函数和 Reduce 函数

Lisp 函数式程序设计语言是一种列表处理语言。Lisp定义了可对列表元素进行整体处理的 各种操作。例如,(add #(1 2 3 4) #(4 3 2 1)) 产生的结果为 #(5 5 5 5)。

Lisp 中还提供了类似于 Map 函数和 Reduce 函数的操作。如:

  • (map 'vector #+ #(1 2 3 4 5) #(10 11 12 13 14)),通过定义加法 Map 运算将两个向量相加产生的结果为 #(11 13 15 17 19)。
  • (reduce #’ + #(11 13 15 17 19)) 通过加法归并产生的累加结果为 75。

Map 函数对一组数据元素进行某种重复式的处理,Reduce 函数对 Map 函数的中间结果进行某种进一步的结果整理。

MapReduce 通过借鉴 Lisp 的思想,定义了 Map 和 Reduce 两个抽象的编程接口,为程序员提供了一个清晰的操作接口抽象描述,由用户去编程实现。

1) Map:<k1,v1>List(<K2,V2>)

输入:键值对<k1,v1>表示的数据。

处理:数据记录将以“键值对”形式传入 Map 函数;Map 函数将处理这些键值对,并以另一种键值对形式输出中间结果 List(<K2,V2>)。

输出:键值对List(<K2,V2>)示的一组中间数据。

2) Reduce:<K2,List(V2)>→List(<K3,V3>)

输入:由 Map 输出的一组键值对 List(<K2,V2>)将被进行合并处理,同样主键下的不同数值会合并到一个列表List(V2)中,故 Reduce 的输入为<K2,List(V2)>。

处理:对传入的中间结果列表数据进行某种整理或进一步的处理,并产生最终的输出结果List(<K3,V3>)。

输出:最终输出结果List(<K3,V3>)。

基于 MapReduce 的并行计算模型如图 3 所示。各个 Map 函数对所划分的数据并行处理,从不同的输入数据产生不同的中间结果。

各个 Reduce 函数也各自并行计算,负责处理不同的中间结果。进行 Reduce 函数处理之前,必须等到所有的 Map 函数完成。

因此,在进入 Reduce 函数前需要有一个同步屏障;这个阶段也负责对 Map 函数的中间结果数据进行收集整理处理,以便 Reduce 函数能更有效地计算最终结果,最终汇总所有 Reduce 函数的输出结果即可获得最终结果。
在这里插入图片描述

3)上升到架构:并行自动化并隐藏底层细节

MapReduce 提供了一个统一的计算框架,来完成计算任务的划分和调度,数据的分布存储和划分,处理数据与计算任务的同步,结果数据的收集整理,系统通信、负载平衡、计算性能优化、系统结点出错检测和失效恢复处理等。

MapReduce 通过抽象模型和计算框架把需要做什么与具体怎么做分开了,为程序员提供了一个抽象和高层的编程接口和框架,程序员仅需要关心其应用层的具体计算问题,仅需编写少量的处理应用本身计算问题的程序代码。

与具体完成并行计算任务相关的诸多系统层细节被隐藏起来,交给计算框架去处理:从分布代码的执行,到大到数千个,小到单个的结点集群的自动调度使用。

MapReduce 计算架构提供的主要功能包括以下几点。

1)任务调度

提交的一个计算作业(Job)将被划分为很多个计算任务(Tasks)。

任务调度功能主要负责为这些划分后的计算任务分配和调度计算结点(Map 结点或 Reduce 结点),同时负责监控这些结点的执行状态,以及 Map 结点执行的同步控制,也负责进行一些计算性能优化处理。例如,对最慢的计算任务采用多备份执行,选最快完成者作为结果。

2)数据/程序互定位

为了减少数据通信量,一个基本原则是本地化数据处理,即一个计算结点尽可能处理其本地磁盘上分布存储的数据,这实现了代码向数据的迁移。

当无法进行这种本地化数据处理时,再寻找其他可用结点并将数据从网络上传送给该结点(数据向代码迁移),但将尽可能从数据所在的本地机架上寻找可用结点以减少通信延迟。

3)出错处理

在以低端商用服务器构成的大规模 MapReduce 计算集群中,结点硬件(主机、兹盘、内存等)出错和软件有缺陷是常态。因此,MapReduce 架构需要能检测并隔离出错结点,并调度分配新的结点接管出错结点的计算任务。

4)分布式数据存储与文件管理

海量数据处理需要一个良好的分布数据存储和文件管理系统作为支撑,该系统能够把海量数据分布存储在各个结点的本地磁盘上,但保持整个数据在逻辑上成为一个完整的数据文件。

为了提供数据存储容错机制,该系统还要提供数据块的多备份存储管理能力。

5)Combiner 和 Partitioner

为了减少数据通信开销,中间结果数据进入 Reduce 结点前需要进行合并(Combine)处理,即把具有同样主键的数据合并到一起避免重复传送。

一个 Reduce 结点所处理的数据可能会来自多个 Map 结点,因此,Map 结点输出的中间结果需使用一定的策略进行适当的划分(Partition)处理,保证相关数据发送到同一个 Reduce 结点上。

在这里插入图片描述
1)分布式的运算程序往往需要分成至少2个阶段。

2)第一个阶段的MapTask并发实例,完全并行运行,互不相干。

3)第二个阶段的ReduceTask并发实例互不相干,但是他们的数据依赖于上一个阶段的所有MapTask并发实例的输出。

4)MapReduce编程模型只能包含一个Map阶段和一个Reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个MapReduce程序,串行运行。

MapReduce进程

一个完整的MapReduce程序在分布式运行时有三类实例进程:

1)MrAppMaster:

​ 负责整个程序的过程调度及状态协调。

2)MapTask:

​ 负责Map阶段的整个数据处理流程。

3)ReduceTask:

​ 负责Reduce阶段的整个数据处理流程。

MapReduce框架原理

在这里插入图片描述
在这里插入图片描述

1)JobClient

用户编写的 MapReduce 程序通过 JobClient 提交给 JobTracker。

2)JobTracker

JobTracker 主要负责资源监控和作业调度,并且监控所有 TaskTracker 与作业的健康情况,一旦有失败情况发生,就会将相应的任务分配到其他结点上去执行。

3)TaskTracker

TaskTraker 会周期性地将本结点的资源使用情况和任务进度汇报给 JobTracker,与此同时会接收 JobTracker 发送过来的命令并执行操作。

4)Task

Task 分为 Map Task 和 Reduce Task 两种,由 TaskTracker 启动,分别执行 Map 和 Reduce 任务。一般来讲,每个结点可以运行多个 Map 和 Reduce 任务。

在这里插入图片描述
在这里插入图片描述
在 Input 阶段,框架根据数据的存储位置,把数据分成多个分片(Splk),在多个结点上并行处理。

Map 任务通常运行在数据存储的结点上,也就是说,框架是根据数据分片的位置来启动 Map 任务的,而不是把数据传输到 Map 任务的位置上。这样,计算和数据就在同一个结点上,从而不需要额外的数据传输开销。

在 Map 阶段,框架调用 Map 函数对输入的每一个 <key,value> 进行处理,也就是完成 Map<K1,V1>→List(<K2,V2>) 的映射操作。图 1 为找每个文件块中每个字母出现的次数,其中,K1 表示字母,V2 表示该字母出现的次数。

在 Sort 阶段,当 Map 任务结束以后,会生成许多 <K2,V2>形式的中间结果,框架会对这些中间结果按照键进行排序。图 1 就是按照字母顺序进行排序的。

在 Combine 阶段,框架对于在 Sort 阶段排序之后有相同键的中间结果进行合并。合并所使用的函数可以由用户进行定义。在图 1 中,就是把 K2 相同(也就是同一个字母)的 V2 值相加的。这样,在每一个 Map 任务的中间结果中,每一个字母只会出现一次。

在 Partition 阶段,框架将 Combine 后的中间结果按照键的取值范围划分为 R 份,分别发给 R 个运行 Reduce 任务的结点,并行执行。分发的原则是,首先必须保证同一个键的所有数据项发送给同一个 Reduce 任务,尽量保证每个 Reduce 任务所处理的数据量基本相同。

在图 1 中,框架把字母 a、b、c 的键值对分别发给了 3 个 Reduce 任务。框架默认使用 Hash 函数进行分发,用户也可以提供自己的分发函数。

在 Reduce 阶段,每个 Reduce 任务对 Map 函数处理的结果按照用户定义的 Reduce 函数进行汇总计算,从而得到最后的结果。在图 1 中,Reduce 计算每个字母在整个文件中出现的次数。只有当所有 Map 处理过程全部结束以后,Reduce 过程才能开始。

在 Output 阶段,框架把 Reduce 处理的结果按照用户指定的输出数据格式写入 HDFS 中。

在 MapReduce 的整个处理过程中,不同的 Map 任务之间不会进行任何通信,不同的 Reduce 任务之间也不会发生任何信息交换。用户不能够显式地从一个结点向另一个结点发送消息,所有的信息交换都是通过 MapReduce 框架实现的。

MapReduce 计算模型实现数据处理时,应用程序开发者只需要负责 Map 函数和 Reduce 函数的实现。MapReduce 计算模型之所以得到如此广泛的应用就是因为应用开发者不需要处理分布式和并行编程中的各种复杂问题。

MapReduce实例分析[帮助理解]

单词计数是最简单也是最能体现 MapReduce 思想的程序之一,可以称为 MapReduce 版“Hello World”。单词计数的主要功能是统计一系列文本文件中每个单词出现的次数。本节通过单词计数实例来阐述采用 MapReduce 解决实际问题的基本思路和具体实现过程。

设计思路

首先,检查单词计数是否可以使用 MapReduce 进行处理。因为在单词计数程序任务中,不同单词的出现次数之间不存在相关性,相互独立,所以,可以把不同的单词分发给不同的机器进行并行处理。因此,可以采用 MapReduce 来实现单词计数的统计任务。

其次,确定 MapReduce 程序的设计思路。把文件内容分解成许多个单词,然后把所有相同的单词聚集到一起,计算出每个单词出现的次数。

最后,确定 MapReduce 程序的执行过程。把一个大的文件切分成许多个分片,将每个分片输入到不同结点上形成不同的 Map 任务。每个 Map 任务分别负责完成从不同的文件块中解析出所有的单词。

Map 函数的输入采用 <key,value> 方式,用文件的行号作为 key,文件的一行作为 value。Map 函数的输出以单词作为 key,1 作为 value,即 <单词,1> 表示该单词出现了 1 次。

Map 阶段结束以后,会输出许多 <单词,1> 形式的中间结果,然后 Sort 会把这些中间结果进行排序并把同一单词的出现次数合并成一个列表,得到 <key,List(value)>形式。例如,<Hello,<1,1,1,1,1>> 就表明 Hello 单词在 5 个地方出现过。

如果使用 Combine,那么 Combine 会把每个单词的 List(value) 值进行合并,得到 <key,value> 形式,例如,<Hello,5> 表明 Hello 单词出现过 5 次。

在 Partition 阶段,会把 Combine 的结果分发给不同的 Reduce 任务。Reduce 任务接收到所有分配给自己的中间结果以后,就开始执行汇总计算工作,计算得到每个单词出现的次数并把结果输出到 HDFS 中。

处理过程

下面通过一个实例对单词计数进行更详细的讲解。

1)将文件拆分成多个分片。

该实例把文件拆分成两个分片,每个分片包含两行内容。在该作业中,有两个执行 Map 任务的结点和一个执行 Reduce 任务的结点。每个分片分配给一个 Map 结点,并将文件按行分割形成 <key,value> 对,如图 1 所示。这一步由 MapReduce 框架自动完成,其中 key 的值为行号。

2)将分割好的 <key,value> 对交给用户定义的 Map 方法进行处理,生成新的 <key,value> 对,如图 2 所示。
在这里插入图片描述
​ 图 1 分割过程
在这里插入图片描述
​ 图 2 执行Map函数

**3)**在实际应用中,每个输入分片在经过 Map 函数分解以后都会生成大量类似 <Hello,1> 的中间结果,为了减少网络传输开销,框架会把 Map 方法输出的 <key,value> 对按照 key 值进行排序,并执行 Combine 过程,将 key 值相同的 value 值累加,得到 Map 的最终输出结果,如图 3 所示。

在这里插入图片描述
​ 图 3 Map端排序及Combine过程

**4)**Reduce 先对从 Map 端接收的数据进行排序,再交由用户自定义的 Reduce 方法进行处理,得到新的 <key,value> 对,并作为结果输出,如图 4 所示。
在这里插入图片描述
​ 图 4 Reduce 端排序及输出结果

MapReduce作业执行流程

整个 Hadoop MapReduce 的作业执行流程如图 所示,共分为 10 步。
在这里插入图片描述
​ Hadoop MapReduce的作业执行流程

1. 提交作业

客户端向 JobTracker 提交作业。首先,用户需要将所有应该配置的参数根据需求配置好。作业提交之后,就会进入自动化执行。在这个过程中,用户只能监控程序的执行情况和强制中断作业,但是不能对作业的执行过程进行任何干预。提交作业的基本过程如下。

**1)**客户端通过 Runjob() 方法启动作业提交过程。

**2)**客户端通过 JobTracker 的 getNewJobId() 请求一个新的作业 ID。

**3)**客户端检查作业的输出说明,计算作业的输入分片等,如果有问题,就抛出异常,如果正常,就将运行作业所需的资源(如作业 Jar 文件,配置文件,计算所得的输入分片等)复制到一个以作业 ID 命名的目录中。

**4)**通过调用 JobTracker 的 submitjob() 方法告知作业准备执行。

2. 初始化作业

JobTracker 在 JobTracker 端开始初始化工作,包括在其内存里建立一系列数据结构,来记录这个 Job 的运行情况。

**1)**JobTracker 接收到对其 submitJob() 方法的调用后,就会把这个调用放入一个内部队列中,交由作业调度器进行调度。初始化主要是创建一个表示正在运行作业的对象,以便跟踪任务的状态和进程。

**2)**为了创建任务运行列表,作业调度器首先从 HDFS 中获取 JobClient 已计算好的输入分片信息,然后为每个分片创建一个 MapTask,并且创建 ReduceTask。

3. 分配任务

JobTracker 会向 HDFS 的 NameNode 询问有关数据在哪些文件里面,这些文件分别散落在哪些结点里面。JobTracker 需要按照“就近运行”原则分配任务。

TaskTracker 定期通过“心跳”与 JobTracker 进行通信,主要是告知 JobTracker 自身是否还存活,以及是否已经准备好运行新的任务等。

JobTracker 接收到心跳信息后,如果有待分配的任务,就会为 TaskTracker 分配一个任务,并将分配信息封装在心跳通信的返回值中返回给 TaskTracker。

对于 Map 任务,JobTracker 通常会选取一个距离其输入分片最近的 TaskTracker,对于 Reduce 任务,JobTracker 则无法考虑数据的本地化。

4.执行任务

**1)**TaskTracker 分配到一个任务后,通过 HDFS 把作业的 Jar 文件复制到 TaskTracker 所在的文件系统,同时,TaskTracker 将应用程序所需要的全部文件从分布式缓存复制到本地磁盘。TaskTracker 为任务新建一个本地工作目录,并把 Jar 文件中的内容解压到这个文件夹中。

**2)**TaskTracker 启动一个新的 JVM 来运行每个任务(包括 Map 任务和 Reduce 任务),这样,JobClient 的 MapReduce 就不会影响 TaskTracker 守护进程。任务的子进程每隔几秒便告知父进程它的进度,直到任务完成。

5. 进程和状态的更新

一个作业和它的每个任务都有一个状态信息,包括作业或任务的运行状态,Map 任务和 Reduce 任务的进度,计数器值,状态消息或描述。任务在运行时,对其进度保持追踪。

这些消息通过一定的时间间隔由 ChildJVM 向 TaskTracker 汇聚,然后再向 JobTracker 汇聚。JobTracker 将产生一个表明所有运行作业及其任务状态的全局视图,用户可以通过 Web UI 进行查看。JobClient 通过每秒查询 JobTracker 来获得最新状态,并且输出到控制台上。

6. 作业的完成

当 JobTracker 接收到的这次作业的最后一个任务已经完成时,它会将 Job 的状态改为“successful”。当 JobClient 获取到作业的状态时,就知道该作业已经成功完成,然后 JobClient 打印信息告知用户作业已成功结束,最后从 Runjob() 方法返回。

MapReduce 的 Shuffle 阶段

​ 在Hadoop这样的集群环境中,大部分map task与reduce task的执行是在不同的节点上。当然很多情况下Reduce执行时需要跨节点去拉取其它节点上的map task结果。如果集群正在运行的job有很多,那么task的正常执行对集群内部的网络资源消耗会很严重。这种网络消耗是正常的,我们不能限制,能做的就是最大化地减少不必要的消耗。还有在节点内,相比于内存,磁盘IO对job完成时间的影响也是可观的。从最基本的要求来说,我们对Shuffle过程的期望可以有:

  1. 完整地从map task端拉取数据到reduce 端。
  2. 在跨节点拉取数据时,尽可能地减少对带宽的不必要消耗。
  3. 减少磁盘IO对task执行的影响。

在这里插入图片描述

Hadoop MapReduce 的 Shuffle 阶段是指从 Map 的输出开始,包括系统执行排序,以及传送 Map 输出到 Reduce 作为输入的过程。

排序阶段是指对 Map 端输出的 Key 进行排序的过程。不同的 Map 可能输出相同的 Key,相同的 Key 必须发送到同一个 Reduce 端处理。Shuffle 阶段可以分为 Map 端的 Shuffle 阶段和 Reduce 端的 Shuffle 阶段。Shuffle 阶段的工作过程,如图 2 所示。

1. Map 端的 Shuffle 阶段

**1)**每个输入分片会让一个 Map 任务来处理,默认情况下,以 HDFS 的一个块的大小(默认为 64MB)为一个分片。Map 函数开始产生输出时,并不是简单地把数据写到磁盘中,因为频繁的磁盘操作会导致性能严重下降。它的处理过程是把数据首先写到内存中的一个缓冲区, 并做一些预排序,以提升效率。

**2)**每个 Map 任务都有一个用来写入输出数据的循环内存缓冲区(默认大小为 100MB),当缓冲区中的数据量达到一个特定阈值(默认是 80%)时,系统将会启动一个后台线程,把缓冲 区中的内容写到磁盘中(即 Spill 阶段)。在写磁盘过程中,Map 输出继续被写到缓冲区中,但如果在此期间缓冲区被填满,那么 Map 任务就会阻塞直到写磁盘过程完成。

**3)**在写磁盘前,线程首先根据数据最终要传递到的 Reduce 任务把数据划分成相应的分区(Partition)。在每个分区中,后台线程按 Key 进行排序,如果有一个 Combiner,便会在排序后 的输出上运行。

**4)**一旦内存缓冲区达到溢出写的阈值,就会创建一个溢出写文件,因此在 Map 任务完成其最后一个输出记录后,便会有多个溢出写文件。在 Map 任务完成前,溢出写文件被合并成一个索引文件和数据文件(多路归并排序)(Sort 阶段)。

**5)**溢出写文件归并完毕后,Map 任务将删除所有的临时溢出写文件,并告知 TaskTracker 任务已完成,只要其中一个 Map 任务完成,Reduce 任务就会开始复制它的输出(Copy 阶段)。

**6)**Map 任务的输出文件放置在运行 Map 任务的 TaskTracker 的本地磁盘上,它是运行 Reduce 任务的 TaskTracker 所需要的输入数据。

2. Reduce 端的 Shuffle 阶段

**1)**Reduce 进程启动一些数据复制线程,请求 Map 任务所在的 TaskTracker 以获取输出文件(Copy 阶段)。
**2)**将 Map 端复制过来的数据先放入内存缓冲区中,Merge 有 3 种形式,分别是内存到内存,内存到磁盘,磁盘到磁盘。默认情况下,第一种形式不启用,第二种形式一直在运行(Spill 阶段),直到结束,第三种形式生成最终的文件(Merge 阶段)。
**3)**最终文件可能存在于磁盘中,也可能存在于内存中,但是默认情况下是位于磁盘中的。当 Reduce 的输入文件已定,整个 Shuffle 阶段就结束了,然后就是 Reduce 执行,把结果放到 HDFS 中(Reduce 阶段)。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值