Hadoop笔记

一、HDFS

1、概述

HDFS是Hadoop项目的核心子项目,是Hadoop主要应用的一个分布式文件系统。实际上,在Hadoop中有一个综合性的文件系统抽象,它提供了文件系统实现的各类接口,HDFS只是这一个抽象文件系统的一个实例。

2、Hadoop文件系统

Hadoop整合了众多文件系统,它首先提供了一个高层的文件系统抽象类org.apache.hadoop.fs.FileSystem,这个抽象类展示了一个分布式文件系统,并有几个具体实现,如下:
Hadoop笔记

3、特性

(1)处理超大文件
(2)流式的访问数据,请求整个数据集要比读取一条记录更加高效
(3)运行于廉价的商用机器集群上

4、局限性

(1)不适合低延迟数据访问
HDFS是为处理大型数据集分析任务的,主要是为达到高的数据吞吐量而设计的,这就可能要求以高延迟作为代价。可以尝试用Hbase来替代。
(2)无法高效存储小文件
在Hadoop中需要用NameNode(名称节点)来管理文件系统的元数据,如果文件数量太多的话NameNode需要管理的元数据体积就会很大,这样NameNode工作的压力很大,检索处理元数据的时间就不可接受了;同时,NameNode将元数据存储在内存中,因此所能存储的文件数量上限受到NameNode内存容量的限制。每个文件、目录和数据块的存储信息大约占用150字节,如果有一百万个文件,且每个文件占用一个数据块,那至少需要300 MB的内存。
(3)不支持多用户写入及任意修改文件
在HDFS的一个文件中只有一个写入者,而且写操作只能在文件末尾完成,即只能执行追加操作。
(4)不适用于频繁写入的场景
适合“一次写入,多次读取”的场景。

5、体系结构

5.1 块

(1)在操作系统中都有一个文件块的概念,文件以块的形式存储在磁盘中,此处块的大小代表系统读/写可操作的最小文件大小。也就是说,文件系统每次只能操作磁盘块大小的整数倍数据。通常来说,一个文件系统块为几千字节,而磁盘块大小为512字节。
(2)HDFS中的块是一个抽象的概念,默认配置为128M。和单机中的文件系统相同,HDFS分布式文件系统中的文件也被分成块进行存储,它是文件存储处理的逻辑单元。不同的是,小于一个块大小的文件不会占据整个块的大小(一个1MB大小的文件,存储在128MB的块中时,不会占用128MB的空间,只占用1MB的空间)。HDFS中的块设计的这么大,是为了最小化寻址开销,如果快足够大,那么从磁盘传输块的时间会明显大于寻找这个块所花费的寻址时间。
(3)引入块概念的好处
冲破单点存储空间的限制,将文件分成众多块,分别存储在集群中的各台集群上;
块的大小固定,简化了存储系统的管理,特别是元数据和文件块内容可以分开存储;
块有利于分布式文件系统中复制容错的实现,默认文件快副本数为3,分别存储在集群不同的节点上。当一个块损坏时,系统会通过NameNode获取元数据信息,在另外的机器上读取一个副本并进行存储,这个过程对用户是透明。
(4)块缓存
对于频繁访问的文件,用户或者应用可以设置块缓存,这样该文件对应的块会被缓存到DataNode的内存中。默认情况下,一个块只会缓存到一个datanode中,当然也可以针对每个文件设置缓存datanode的数量。用户或者应用通过在缓存池中增加一个cache directive来告诉namenode需要缓存那些文件以及存多久。

5.2 NameNode和DataNode

HDFS中的节点分为NameNode和DataNode这两种节点。这两类节点分别承担Master和Worker的任务。NameNode就是Master管理集群中的执行调度,DataNode就是Worker具体任务的执行节点。NameNode管理文件系统的命名空间,维护整个文件系统的文件目录树及这些文件的索引目录。这些信息以两种方式存储在本地文件系统中,一种是命名空间镜像,一种是编辑日志。NameNode同时也记录着每个文件中各个块所在的数据节点(DataNode)信息,但是这些信息不是永久存储的,NameNode在每次启动系统时根据数据节点信息动态的重建。客户端通过NameNode获取元数据,和DataNode进行交互以访问整个文件系统。DataNode用来执行具体的任务:存储文件块,被客户端和NameNode调用。

Hadoop笔记

5.3 副本的存放与读取策略

按默认配置副本存三份的话,一份存在本地机器,一份存在同一机架的另一节点,另外一份存在不同机架的节点上,这样可以保证本机架损坏的时候还留有副本。如果本地数据损坏的话,先到同一机架的另一节点上去读取,同一机架上读取比较快,如果数据也损坏的话就读取最后一份放到不同机架上的副本。

5.4 安全模式

NameNode重启时首先会进入安全模式,处于安全模式的NameNode是不会进行数据块的复制的。在此阶段DataNode报告其所存储的所有数据块列表。

5.5 文件安全(保障NameNode)

客户端通过NameNode获取文件块的位置,如果NameNode出现故障,就意味着整个文件系统的全部文件丢失。Hadoop采用两种机制来保障NameNode的安全。
(1)备份NameNode上持久化存储的元数据文件,然后将其转储到其他文件系统。
(2)在集群中同步运行一个Secondary NameNode(二级名称空间),这个节点的主要作用是周期性的合并编辑日志中的命名空间镜像,以避免编辑日志过大。Secondary NameNode的同步备份总是落后于NameNode的。

5.6 HDFS HA(NameNode可高可用)

(1)对于只有一个NameNode的集群,如果NameNode机器出现故障,那么整个集群将无法使用,直到NameNode重新启动。
(2)Hadoop2 提供了高可用方案。在这一实现中,配置了一对活动-备用(active-standby)namenode。当活动namenode失效,备用namenode就会接管它的任务并开始服务于来自客户端的请求。实现这一目标需要在原来的架构上做如下修改:
     a、namenode通过高可用共享存储实现编辑日志的共享。当备用namenode接管工作以后,它将通读编辑日志直至末尾,以实现与活动namenode的状态同步。
     b、datanode需要同时向两个namenode发送数据块处理报告。因为数据块的映射信息存储在namenode内存中,而非磁盘。
     c、客户端需要使用特定的机制来处理namenode的失效问题,这一机制对用户是透明的。
     d、辅助namenode的角色被备用namenode所包含,备用namenode为活动namenode命名空间设置周期性检查点。
(3)高可用共享存储推荐用 群体日志管理器(QJM)。QJM以一组日志节点的形式运行,每一次编辑必须写入多数日志节点。典型的,有三个节点,能够忍受其中任何一个的丢失。另外一种高可用共享存储是 NFS过滤器。NFS过滤器并不能保证同一时间只允许一个namenode写入数据,这可能会导致活动namenode失效、备用namenode切换时发生错误。QJM可以保证,推荐用QJM。
(4)系统中有一个称为故障转移控制器,管理着将活动namenode转移为备用namenode的转换过程。默认故障转移控制器使用了zookeeper来实现。每一个namenode运行一个轻量级的故障转移控制器,其工作就是监视宿主namenode是否失效(通过一个简单的心跳机制实现)并在namenode失效时进行故障转移。在某些情况下,无法确切的知道失效namenode是否已经停止运行,例如在网速非常慢或者网络被切割情况下,此时需要高可用共享存储同一时间只允许一个namenode写入数据,来规避两个namenode都写入数据的情况。此时之前失效的活动namenode仍有可能在处理一些读请求,需要人工用ssh命令来杀死进程。

6、HDFS中的读写数据流

6.1 读取流程

Hadoop笔记
客户端向NameNode请求文件块所在的DataNode节点,再分别跟DataNode节点建立连接读取数据。这样就减小了NameNode的压力。

6.2 写入流程

Hadoop笔记

6.3 一致性模型

(1)HDFS一致性模型描述了文件读写的可见性。
(2)当创建一个文件时,它在文件系统的命名空间是立即可见的。
(3)对文件任何的写操作不保证是可见的,即使在数据流已经刷新的情况(调用flush方法)。通过hflush()方法可以强行将所有缓存刷新到datanode内存中,当hflush()方法返回成功,此次写操作对后面的reader可见。但是hflush方法只保证刷新到datanode内存中,不保证datanode会持久化到磁盘。
(4)HDFS提供了hsync()函数,强制datanode刷新内存持久化到磁盘
在HDFS中关闭一个文件也隐式的执行了hsync()函数。
hflush()和hsync()函数会有一些开销。


7、I/O操作中的数据检查

(1) 校验和是检查数据完整性的重要方式,通过比较新旧校验和是否相同,来判断数据是否已经损坏。比如,在传输数据之前生成了一个校验和,将数据传输到目的地后再次计算校验和,如果两者不同说明在传输的过程中数据发生了损坏。Hadoop采用CRC-32(循环冗余校验,其中的32指生成的校验和是32位的)。
(2)Hadoop在写入本地文件、DataNodes之间数据备份传输过程中都会进行数据的校验,并且DataNode后台守护进程也会定期检测。一旦发现有数据损坏,就采用复制完整的备份来恢复数据。

8、HDFS文件结构

8.1 NameNode

格式化的NameNode会创建以下的目录结构:

${dfs.name.dir}/current/VERSION

                       /edits

                       /fsimage

                       /fstime

(1)VERSION
VERSION文件是java属性文件,其中包含运行HDFS的版本信息。一个典型的VERSION文件所包含的内容:

                         #Wed Mar 23 16:03:27 CST 2011

                         namespaceID=123456789               //文件系统唯一标识符,DataNode要保持一致

                         cTime=0   //标记NameNode存储空间创建的时间

                        storageType=NAME_NODE  //指出此存储目录包含一个NameNode的数据结构

                        layoutVersion=-18   //定义了HDFS持久化数据结构的版本

(2)edits(编辑日志)
记录客户端的写操作
(3)fsimage(文件系统映像)
保存内存中元数据的镜像。fsimage文件包含文件系统中所有目录和文件inode的序列化形式。每个inode是一个文件或目录的元数据的内部表示,并包含此类信息:文件的复制等级;修改和访问时间;访问权限;块的大小以及组成文件的块。对于目录,则存储修改时间、权限和配额元数据。

fsimage文件没有记录块存储在哪个数据节点,而是将这种映射保存在内存中,由DataNode来给NameNode汇报自身包含的块列表。

(4)fstime(存储检查点更新时间)

存储检查点更新时间。检查点可以看作是某一个时间点的fsimage。第二名称节点负责维护更新名称节点中的edits和fsimage文件。第二名称节点从名称节点取得edits和fsimage文件,并合并生成新的fsimage文件再发给名称节点,名称节点并启用新的edits文件。

8.2 Secondary NameNode

Secondary NameNode(第二名称节点)包含以下目录:

 ${fs.checkpoint.dir}/current/VERSION

                              /edits

                              /fsimage

                              /fstime

                              /previous.checkpoint/VERSION

                                                  /edits

                                                  /fsimage

                                                  /fstime

previous.checkpoint目录存储上一次生成的检查点,此目录为备份目录,它的目录布局与名称节点的布局完全一样。

8.3 DataNode数据节点

DataNode不用像NameNode那样明确格式化(通过格式化命令),DataNode会在启动时创建自己的存储目录。

关键的文件和目录如下:


                        ${dfs.data.dir}/current/VERSION

                                               /blk_<id_1>

                                               /blk_<id_1>.meta

                                               /blk_<id_2>(块本身,块数据)

                                               /blk_<id_2>.meta(块的元数据)

                                               ......

                                                /blk_<id_64>  

                                                /blk_<id_64>.meta

                                                /subdir0/(当块太多时,新建一个子目录,默认块数达到64时)

                                                /subdir1/

                                                ....

                                                /subdir63/

数据节点的VERSION文件跟名称节点的VERSION文件十分类似:

                    #Tue Mar 10 21:32:21 GMT 2009

                      namespaceID=123456789

                      storageID=DS-547717739-172.16.85.1-50010-1236720751627

                      cTime=0

                      storageType=DATA_NODE

                      layoutVersion=-18

9、SequenceFile和MapFile

HDFS和MapReduce子框架主要是针对大文件来设计的,在小文件的处理上不但效率低下,而且十分消耗内存资源(每一个小文件占用一个Block,每一个block的元数据都存储在namenode的内存里)。解决办法通常是选择一个容器,将这些小文件组织起来统一存储。HDFS提供了两种类型的容器,分别是SequenceFile和MapFile。

9.1 SequenceFile

SequenceFile的存储类似于Log文件,所不同的是Log File的每条记录的是纯文本数据,而SequenceFile的每条记录是可序列化的字符数组。

SequenceFile可通过如下API来完成新记录的添加操作:


fileWriter.append(key,value);

可以看到,每条记录以键值对的方式进行组织,但前提是Key和Value需具备序列化和反序列化的功能

Hadoop预定义了一些Key Class和Value Class,他们直接或间接实现了Writable接口,满足了该功能,包括:


Text 等同于java中的String
IntWritable 等同于Java中的Int
BooleanWritable
等同于Java中的Boolean

在存储结构上,SequenceFile主要由一个Header后跟多条Record组成,如图所示:
Hadoop笔记
Header主要包含了Key classname,Value classname,存储压缩算法,用户自定义元数据等信息,此外,还包含了一些同步标识,用于快速定位到记录的边界。
每条Record以键值对的方式进行存储,用来表示它的字符数组可依次解析成:记录的长度、Key的长度、Key值和Value值,并且Value值的结构取决于该记录是否被压缩。
数据压缩有利于节省磁盘空间和加快网络传输,SeqeunceFile支持两种格式的数据压缩,分别是:record compression和block compression。
(1)record compression如上图所示,是对每条记录的value进行压缩。
(2)block compression是将一连串的record组织到一起,统一压缩成一个block,如图所示:
Hadoop笔记
block信息主要存储了:块所包含的记录数、每条记录Key长度的集合、每条记录Key值的集合、每条记录Value长度的集合和每条记录Value值的集合 。

9.2 MapFile

(1)MapFile是排序后的SequenceFile,通过观察其目录结构可以看到MapFile由两部分组成,分别是data和index。

(2)index作为文件的数据索引,主要记录了每个Record的key值,以及该Record在文件中的偏移位置。在MapFile被访问的时候,索引文件会被加载到内存,通过索引映射关系可迅速定位到指定Record所在文件位置,因此,相对SequenceFile而言,MapFile的检索效率是高效的,缺点是会消耗一部分内存来存储index数据。

(3)MapFile并不会把所有Record都记录到index中去,默认情况下每隔128条记录存储一个索引映射。当然,记录间隔可人为修改,通过MapFIle.Writer的setIndexInterval()方法,或修改io.map.index.interval属性;

(4)与SequenceFile不同的是,MapFile的KeyClass一定要实现WritableComparable接口,即Key值是可比较的。


注意:使用MapFile或SequenceFile虽然可以解决HDFS中小文件的存储问题,但也有一定局限性,如:
a、文件不支持复写操作,不能向已存在的SequenceFile(MapFile)追加存储记录

b、当write流不关闭的时候,没有办法构造read流。也就是在执行文件写操作的时候,该文件是不可读取的



二、YARN

yarn是hadoop集群资源管理系统,在hadoop 2.0被引入,用来改善MapReduce,但是它具有通用性,也可以支持其他分布式计算模式,例如Spark、Tez等。

1、MapReduce局限性

在MapReduce框架中,作业执行受两种类型的进程控制:
(1)一个称为 JobTracker 的主要进程,它协调在集群上运行的所有作业,分配要在 TaskTracker 上运行的 map 和 reduce 任务。
(2)许多称为 TaskTracker 的下级进程,它们运行分配的任务并定期向 JobTracker 报告进度。

这种设计存在三种局限性:
(1)可扩展性。当节点数达到4000,任务数达到40000时,MapReduce会存在扩展性瓶颈。原因在于JobTracker同时管理作业和任务
(2)利用率。 在 Hadoop MapReduce 中,每个从属节点上的计算资源由集群管理员分解为固定数量的 map 和 reduce slot,这些 slot 不可替代。设定 map slot 和 reduce slot 的数量后,节点在任何时刻都不能运行比 map slot 更多的 map 任务,即使没有 reduce 任务在运行。这影响了集群的利用率,因为在所有 map slot 都被使用(而且我们还需要更多)时,我们无法使用任何 reduce slot,即使它们可用,反之亦然。
(3)Hadoop 设计为仅运行 MapReduce 作业。随着替代性的编程模型(比如 Apache Giraph 所提供的图形处理)的到来,除 MapReduce 外,越来越需要为可通过高效的、公平的方式在同一个集群上运行并共享资源的其他编程模型提供支持。

2、YARN的架构

Hadoop笔记

在 YARN 架构中,一个全局 ResourceManager 以主要后台进程的形式运行,它通常在专用机器上运行,在各种竞争的应用程序之间仲裁可用的集群资源。ResourceManager 会追踪集群中有多少可用的活动节点和资源,协调用户提交的哪些应用程序应该在何时获取这些资源。ResourceManager 是惟一拥有此信息的进程,所以它可通过某种共享的、安全的、多租户的方式制定分配(或者调度)决策(例如,依据应用程序优先级、队列容量、ACLs、数据位置等)。

在用户提交一个应用程序时,一个称为 ApplicationMaster 的轻量型进程实例会启动来协调应用程序内的所有任务的执行。这包括监视任务,重新启动失败的任务,推测性地运行缓慢的任务,以及计算应用程序计数器值的总和。这些职责以前分配给所有作业的单个 JobTracker。ApplicationMaster 和属于它的应用程序的任务,在受 NodeManager 控制的资源容器中运行。

NodeManager 是 TaskTracker 的一种更加普通和高效的版本。没有固定数量的 map 和 reduce slots,NodeManager 拥有许多动态创建的资源容器。容器的大小取决于它所包含的资源量,比如内存、CPU、磁盘和网络 IO。目前,仅支持内存和 CPU (YARN-3)。未来可使用 cgroups 来控制磁盘和网络 IO。一个节点上的容器数量,由配置参数与专用于从属后台进程和操作系统的资源以外的节点资源总量(比如总 CPU 数和总内存)共同决定。

有趣的是,ApplicationMaster 可在容器内运行任何类型的任务。例如,MapReduce ApplicationMaster 请求一个容器来启动 map 或 reduce 任务,而 Giraph ApplicationMaster 请求一个容器来运行 Giraph 任务。您还可以实现一个自定义的 ApplicationMaster 来运行特定的任务,进而发明出一种全新的分布式应用程序框架,改变大数据世界的格局。您可以查阅 Apache Twill,它旨在简化 YARN 之上的分布式应用程序的编写。

在 YARN 中,MapReduce 降级为一个分布式应用程序的一个角色(但仍是一个非常流行且有用的角色),现在称为 MRv2。MRv2 是经典 MapReduce 引擎(现在称为 MRv1)的重现,运行在 YARN 之上。


3、使用YARN的好处

(1)扩展性好
YARN 能够运行在比 MR1 更大的集群上。 MR1 的上限是 4000 节点 与 40000 个 task。而 YARN 的上限是 10000 个节点与 100000 个 task。
(2)高可用性

在 MR1 中,job 的复杂的状态的改变都发生在 jobtracker 中的内存中,一旦 jobtracker 坏了,就很难再恢复。

而在 YARN 里,jobtracker 的职责分给了 resource manager 和 application master。这样可以为两个组件分别提供高可用。对每个组件发生的错误分别处理,互相不影响。

(3)资源利用率高

在 MR1 中, 每一个 tasktracker 在配置阶段会固定的为 map 或者 reduce task 静态的分配资源,也就是 “槽”。因为每个应用程序所需要的资源并不是完全一样,这样有时会造成资源分配过大,资源浪费,资源分配过小,会导致任务失败。

在 YARN 中,资源由 resource manager 管理,应用程序会请求他所需要的资源。

(4)多应用

YARN 中最大的好处就是可以在其上开启除 Mapreduce 之外的分布式应用程序,比如 Spark。

也可能为用户在一个 YARN 上运行不同版本的 MapReduce。


4、YARN 与 MapReduce 1 对应组件

Hadoop笔记

5、调度器

Yarn提供了三种调度器可供选择:
(1)FIFO调度器
将应用放置在一个队列中,然后按照提交的顺序(先进先出)运行应用。它的优点是简单易懂,不需要任何配置,但是不适合共享集群。大的应用会占用集群中的所有资源,所以每个应用必须等待直到轮到自己运行。在一个共享集群中,更适合使用容量调度器或公平调度器。这两种调度器都允许长时间运行的作业能及时完成,同时也允许正在运行较小临时查询的用户能够在合理时间内得到返回结果。
(2)容量调度器
使用容量调度器时,一个独立的专门队列保证小作业一提交就可以启动,由于队列容量是为那个队列中的作业所保留的,因此这种策略是以真个集群的利用率为代价的
(3)公平调度器
使用公平调度器时,不需要预留一定量的资源,因为调度器会在所有运行的作业之间动态平衡资源。第一个(大)作业启动时,它也是唯一运行的作业,因而获得集群中所有的资源。当第二个(小)作业启动时,它也被分配到集群的一半资源,这样每个作业都能公平共享资源。


上一篇:源码梳理——Jedis中的集合JedisByteHashMap


下一篇:源码梳理——Jedis从缓存池中获取资源和销毁资源