1 HDFS简介
1.1 Hadoop 2.0介绍
Hadoop是Apache的一个分布式系统基础架构,可以为海量数据提供存储和计算。Hadoop 2.0即第二代Hadoop系统,其框架最核心的设计是HDFS、MapReduce和YARN。其中,HDFS为海量数据提供存储,MapReduce用于分布式计算,YARN用于进行资源管理。
Hadoop 1.0和Hadoop 2.0的结构对比:
Hadoop 2.0的主要改进有:
1、通过YARN实现资源的调度与管理,从而使Hadoop 2.0可以运行更多种类的计算框架,如Spark等。
2、实现了NameNode的HA方案,即同时有2个NameNode(一个Active另一个Standby),如果ActiveNameNode挂掉的话,另一个NameNode会转入Active状态提供服务,保证了整个集群的高可用。
3、实现了HDFS federation,由于元数据放在NameNode的内存当中,内存限制了整个集群的规模,通过HDFS federation使多个NameNode组成一个联邦共同管理DataNode,这样就可以扩大集群规模。
4、Hadoop RPC序列化扩展性好,通过将数据类型模块从RPC中独立出来,成为一个独立的可插拔模块。
5 YARN(Yet another resource negotiator)另一种资源协调,基本思想将jobTrack 两个功能资源管理的作业调度/监控 分离,创建一个全局的resourceManager 在namenode上
RM和若干个针对应用程序的applicationmaster在datanode上 AM>>TaskTrack RM>>jontrack
1.2 HDFS概述
HDFS是一个分布式文件系统,具有高容错的特点。它可以部署在廉价的通用硬件上,提供高吞吐率的数据访问,适合需要处理海量数据集的应用程序。
主要特点:
1、支持超大文件:支持TB级的数据文件。
2、检测和快速应对硬件故障:HDFS的检测和冗余机制很好克服了大量通用硬件平台上的硬件故障问题。
3、高吞吐量:批量处理数据。
4、简化一致性模型:一次写入多次读取的文件处理模型有利于提高吞吐量。
HDFS不适合的场景:低延迟数据访问;大量的小文件;多用户写入文件、修改文件。
HDFS的构成:NameNode保存着HDFS的名字空间,对于任何对文件系统元数据产生修改的操作;DataNode将HDFS数据以文件的形式存储在本地文件系统中,它并不知道有关HDFS文件的信息。
数据块:数据块是HDFS的文件存储处理单元,在Hadoop 2.0中默认大小为128MB,可根据业务情况进行配置。数据块的存在,使得HDFS可以保存比存储节点单一磁盘大的文件,而且简化了存储管理,方便容错,有利于数据复制。
1.3 HDFS读写流程
读文件的流程:1、客户端client使用open函数打开文件;2、DistributedFileSystem用RPC调用元数据节点,得到文件的数据块信息;3、对于每一个数据块,元数据节点返回保存数据块的数据节点的地址;4、DistributedFileSystem返回FSDataInputStream给客户端,用来读取数据;5、客户端调用FSDataInputStream的read函数开始读取数据;6、FSDataInputStream连接保存此文件第一个数据块的最近的数据节点;7、Data从数据节点读到客户端;8、当此数据块读取完毕时,FSDataInputStream关闭和此数据节点的连接,然后连接此文件下一个数据块的最近的数据节点;9、当客户端读取数据完毕时,调用FSDataInputStream的close函数;10、在读取数据的过程中,如果客户端在与数据节点通信出现错误,则尝试连接包含此数据块的下一个数据节点。失败的数据节点将被记录,以后不再连接。HDFS的读文件流程如下图所示:
写文件的流程:1、客户端client调用create函数创建文件;2、DistributedFileSystem用RPC调用元数据节点,在文件系统的命名空间中创建一个新的文件;3、元数据节点首先确定文件是否存在,并且客户端是否有创建文件的权限,然后创建新文件;4、DistributedFileSystem返回FSDataOutputStream给客户端用于写数据;5、客户端开始写入数据,FSDataOutputStream将数据分成块,写入data queue;6、Data queue由DataStreamer读取,并通知元数据节点分配数据节点,用来存储数据块(每块默认复制3块),分配的数据节点放在一个pipeline里;7、DataStreamer将数据块写入pipeline中的第一个数据节点,第一个数据节点将数据块发送给第二个数据节点,第二个数据节点将数据发送给第三个数据节点;8、FSDataOutputStream为发出去的数据块保存了ack queue,等待pipeline中的数据节点告知数据已经写入成功;9、如果数据节点在写入的过程中失败,则进行以下几个操作:一是关闭pipeline并将ack queue中的数据块放入data queue的开始;二是当前数据块在已写入的数据节点中被元数据节点赋予新的标示,错误节点重启后察觉其数据块过时而被删除;三是失败的数据节点从pipeline中移除,另外的数据块则写入pipeline中的另外两个数据节点;四是元数据节点被通知此数据块的复制块数不足,从而再创建第三份备份;10、当客户端结束写入数据,则调用close函数将所有的数据块写入pipeline中的数据节点,并等待ack queue返回成功,最后通知元数据节点写入完毕。HDFS的写文件流程如下图所示:
2 YARN原理介绍
2.1 YARN产生背景
Hadoop 1.0的弊端包括:
1、扩展性差:JobTracker同时兼备了资源管理和作业控制两个功能,这是整个系统的最大瓶颈,它严重制约了整个集群的扩展性。
2、可靠性差:JobTracker存在单点故障,JobTracker出现问题将导致整个集群不可用。
3、资源利用率低:资源无法在多个任务间共享或合理分配,导致无法有效利用各种资源。
4、无法支持多种计算框架:Hadoop 1.0只支持MapReduce这种离线批处理计算模式,而无法支持内存计算、流式计算、迭代式计算等。
正是由于Hadoop 1.0存在以上这些弊端,所以Hadoop 2.0推出了资源管理器YARN,有效解决了上述问题。
2.2 YARN基本架构
YARN是Hadoop 2.0的资源管理器。它是一个通用的资源管理系统,可为上层应用提供统一的资源管理和调度,它的引入为集群在利用率、资源统一管理和数据共享等方面带来了巨大好处。
YARN的基本设计思想是将Hadoop 1.0中的JobTracker拆分成了两个独立的服务:一个全局的资源管理器ResourceManager和每个应用程序特有的ApplicationMaster。其中ResourceManager负责整个系统的资源管理和分配,而ApplicationMaster负责单个应用程序的管理,其基本架构如下图所示:
YARN总体上仍然是Master/Slave结构。在整个资源管理框架中,ResourceManager为Master,NodeManager为Slave,并通过HA方案实现了ResourceManager的高可用。ResourceManager负责对各个NodeManager上的资源进行统一管理和调度。当用户提交一个应用程序时,需要提供一个用以跟踪和管理这个程序的ApplicationMaster,它负责向ResourceManager申请资源,并要求NodeManger启动可以占用一定资源的任务。由于不同的ApplicationMaster被分布到不同的节点上,因此它们之间不会相互影响。
ResourceManager:它是一个全局的资源管理器,负责整个系统的资源管理和分配,主要由调度器和应用程序管理器两个组件构成。
调度器:根据容量、队列等限制条件,将系统中的资源分配给各个正在运行的应用程序。调度器仅根据应用程序的资源需求进行资源分配,而资源分配单位用一个抽象概念“资源容器”(简称Container)表示,Container是一个动态资源分配单位,它将内存、CPU、磁盘、网络等资源封装在一起,从而限定每个任务使用的资源量。
应用程序管理器:负责管理整个系统中所有的应用程序,包括应用程序提交、与调度器协商资源以启动ApplicationMaster、监控ApplicationMaster运行状态并在失败时重新启动它等。
ApplicationMaster:用户提交的每个应用程序均包含1个ApplicationMaster,主要功能包括与ResourceManager调度器协商以获取资源、将得到的任务进一步分配给内部的任务、与NodeManager通信以启动/停止任务、监控所有任务运行状态并在任务运行失败时重新为任务申请资源以重启任务等。
NodeManager:它是每个节点上的资源和任务管理器,它不仅定时向ResourceManager汇报本节点上的资源使用情况和各个Container的运行状态,还接收并处理来自ApplicationMaster的Container启动/停止等各种请求。
Container:它是YARN中的资源抽象,它封装了某个节点上的多维度资源,如内存、CPU、磁盘、网络等,当ApplicationMaster向ResourceManager申请资源时,返回的资源便是用Container表示的。YARN会为每个任务分配一个Container,且该任务只能使用该Container中描述的资源。
2.3 YARN工作流程
YARN的工作流程如下图所示:
步骤1:用户向YARN中提交应用程序,其中包括用户程序、ApplicationMaster程序、ApplicationMaster启动命令等。
步骤2:ResourceManager为应用程序分配第一个Container,并与对应的NodeManager通信,要求它在这个Container中启动应用程序的ApplicationMaster。
步骤3:ApplicationMaster首先向ResourceManager注册,这样用户可以直接通过ResourceManager查看应用程序的运行状态,然后ApplicationMaster为各个任务申请资源,并监控它们的运行状态,直到运行结束,即重复步骤4-7。
步骤4:ApplicationMaster采用轮询的方式通过RPC协议向ResourceManager申请和领取资源。
步骤5:一旦ApplicationMaster成功申请到资源,便开始与对应的NodeManager通信,要求它启动任务。
步骤6:NodeManager为任务设置好运行环境(包括环境变量、JAR包、二进制程序等)后,将任务启动命令写到一个脚本中,并通过运行该脚本启动任务。
步骤7:各个任务通过某个RPC协议向ApplicationMaster汇报自己的状态和进度,使ApplicationMaster能够随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务。在应用程序运行过程中,用户可随时通过RPC向ApplicationMaster查询应用程序的当前运行状态。
步骤8:应用程序运行完成后,ApplicationMaster通过RPC协议向ResourceManager注销并关闭自己。
3 MapReduce原理介绍
3.1 MapReduce介绍
MapReduce是由Google公司研究提出的一种面向大规模数据处理的并行计算模型和方法,是Hadoop面向大数据并行处理的计算模型、框架和平台。
MapReduce执行流包括input、map、shuffle、reduce和output共5个过程,如下图所示:
3.2MapReduce2运行原理
YARN框架下的Mapreduce工作流程如下图所示:
步骤1:客户端向集群提交作业。
步骤2:Job从ResourceManager获取新的作业应用程序ID。
步骤3:客户端检查作业的输出情况,计算输入分片,并将作业jar包、配置、分片信息等作业资源复制到HDFS。
步骤4:Job向ResourceManager提交作业。
步骤5:ResourceManager接收到作业后,将作业请求传递给调度器,调度器根据作业信息为ResourceManager分配一个container,然后ResourceManager在NodeManager的管理下,在container中启动一个ApplicationMaster进程。
步骤6:ApplicationMaster对作业进行初始化,并保持对作业的跟踪,判断作业是否完成。
步骤7:ApplicationMaster根据存储在HDFS中的分片信息确定Map和Reduce的数量。
步骤8:ApplicationMaster为本次作业的Map和Reduce以轮询的方式向ResourceManager申请container。
步骤9:ApplicationMaster获取到container后,与NodeManager进行通信启动container。
步骤10:container从HDFS中获取作业的jar包、配置和分布式缓存文件等,将任务需要的资源本地化。
步骤11:container启动Map或Reduce任务。
3.3 shuffle及排序
Mapreduce的map端输出作为输入传递给reduce端,并按键排序的过程称为shuffle。shuffle的字面含义是洗牌,即将map产生的数据通过分区、排序等过程分配给了不同的reduce任务。Mapreduce的数据处理流程如下图所示
input:默认为textinputformat 在输入前需预先处理一下,mapreduce对少量的大文件很有效,对大量的小文件就力不从心了,用combinfile合并或其他方法,尽量使一个块上的文件为block大小即128m,一个输入分片小于128就是一个map
Map阶段:
1、每个输入分片会让一个map任务来处理,默认情况下,以HDFS的一个块的大小(默认为64M,可设置,二代128M)为一个分片。map输出的结果会暂时放在一个环形内存缓冲区中(该缓冲区的大小默认为100M,由io.sort.mb属性控制)。当该缓冲区快要溢出时(默认为缓冲区大小的80%,由io.sort.spill.percent属性控制),会在本地文件系统中创建一个溢出文件,将该缓冲区中的数据写入这个文件,
2、在写入磁盘之前,线程首先根据reduce任务的数目将数据划分为相同数目的分区,也就是一个reduce任务对应一个分区的数据。这样做是为了避免有些reduce任务分配到大量数据,而有些reduce任务却分到很少数据,甚至没有分到数据的尴尬局面。然后对每个分区中的数据进行排序,如果此时设置了Combiner,将排序后的结果进行combine操作,这样做可以有效减少磁盘IO和网络IO。
3、当map任务输出最后一个记录时,可能会有很多的溢出文件,这时需要将这些文件合并。合并的过程中会不断地进行排序和combine操作,这样做是为了尽量减少每次写入磁盘的数据量和尽量减少下一复制阶段网络传输的数据量。最后合并成了一个已分区且已排序的文件。为了减少网络传输的数据量,这里可以将数据压缩,只要将mapred.compress.map.out设置为true就可以了。
写磁盘前,要进行partition(一个partition对应一个reduce,在全排序时可重写或者指定哪一个reduce)、sort和combine等操作。通过分区,将不同类型的数据分开处理,之后对不同分区的数据进行 排序,如果有Combiner,还要对排序后的数据进行combine(其实和reduce差不多,但慎用必须确保不会对最终结果影响,一般用在求最值 计数上,不可用在求平均数上举例 <word,1>,<hello,1>,<wor,1>combin<word,2> <hello,1>)。等最后记录写完,将全部溢出文件合并为一个分区且排序的文件。
4、将分区中的数据拷贝给相对应的reduce任务。那么分区中的数据如何知道它对应的reduce是哪个呢? ApplicationMaster保存了整个作业的宏观信息,只要reduce任务向ApplicationMaster获取对应的map输出位置就可以了。
补充:在写磁盘的时候采用压缩的方式将map的输出结果进行压缩是一个减少网络开销很有效的方法!
总结:map过程的输出是写入本地磁盘而不是HDFS,但是一开始数据并不是直接写入磁盘而是缓冲在内存中,缓存的好处就是减少磁盘I/O的开销,提高合并和排序的速度。又因为默认的内存缓冲大小是100M(当然这个是可以配置的),所以在编写map函数的时候要尽量减少内存的使用,为shuffle过程预留更多的内存,因为该过程是最耗时的过程
Reduce阶段:
1、Reduce会接收到不同map任务传来的数据,并且每个map传来的数据都是有序的。如果reduce接受的数据量相当小,则直接存储在内存中,如果数据量超过了该缓冲区大小的一定比例,则对数据合并后溢写到磁盘中。
2、随着溢写文件的增多,后台线程会将它们合并成一个更大的有序文件,这样做是为了给后面的合并节省时间。其实不管在map端还是reduce端,MapReduce都是反复地执行排序、合并操作,所以说排序是hadoop的灵魂。
3、在合并的过程中会产生许多的中间文件(写入磁盘了),但MapReduce会让写入磁盘的数据尽可能地少,并且最后一次合并的结果并没有写入磁盘,而是直接输入到reduce函数。
(1)Copy阶段:Reducer通过Http方式得到输出文件的分区。
reduce端可能从n个map的结果中获取数据,而这些map的执行速度不尽相同,当其中一个map运行结束时,reduce就会从 JobTracker中获取该信息。map运行结束后TaskTracker会得到消息,进而将消息汇报给JobTracker,reduce定时从 JobTracker获取该信息,reduce端默认有5个数据复制线程从map端复制数据。
(2)Merge阶段:如果形成多个磁盘文件会进行合并
从map端复制来的数据首先写到reduce端的缓存中,同样缓存占用到达一定阈值后会将数据写到磁盘中,同样会进行partition、 combine、排序等过程。如果形成了多个磁盘文件还会进行合并,最后一次合并的结果作为reduce的输入而不是写入到磁盘中。
(3)Reducer的参数:最后将合并后的结果作为输入传入Reduce任务中。
总结:当Reducer的输入文件确定后,整个Shuffle操作才最终结束。之后就是Reducer的执行了,最后Reducer会把结果存到HDFS上。
output
三、Hadoop中的压缩
刚刚我们在了解Shuffle过程中看到,map端在写磁盘的时候采用压缩的方式将map的输出结果进行压缩是一个减少网络开销很有效的方法。其实,在Hadoop中早已为我们提供了一些压缩算法的实现,我们不用重复造轮子了。
3.1 解压缩算法的实现:Codec
Codec是Hadoop中关于压缩,解压缩的算法的实现,在Hadoop中,codec由CompressionCode的实现来表示。下面是一些常见压缩算法实现,如下图所示:
3.2 MapReduce的输出进行压缩
(1)MapReduce的输出属性如下所示
(2)在Java中如何针对输出设置压缩 ★★★
上图中在reduce端输出压缩使用了刚刚Codec中的Gzip算法,当然你也可以使用bzip2算法;