Hadoop集群具体来说包含两个集群:HDFS集群和YARN集群,两者逻辑上分离,但物理上常在一起。
- HDFS集群:负责海量数据的存储,集群中的角色主要有 NameNode / DataNode / SecondaryNameNode。
- YARN集群:负责海量数据运算时的资源调度,集群中的角色主要有 ResourceManager/NodeManager
- MapReduce:它其实是一个应用程序开发包。
一、HDFS架构
HDFS是一个高度容错性的系统,采用master/slave架构。一个HDFS集群是由一个Namenode和一定数目的Datanodes组成。
- NameNode是一个中心服务器,负责管理文件系统的名字空间(namespace)以及客户端对文件的访问。NameNode可以被认为是系统的主站。它维护所有系统中存在的文件和目录的文件系统树和元数据 。
- DataNode一般是一个节点一个,负责管理它所在节点上的存储。
架构如下图:
1.1 NameNode
- NameNode元数据信息
包括:文件名,文件目录结构,文件属性(生成时间,副本数,权限)每个文件的块列表。
列表中的块与块所在的DataNode之间的地址映射关系。在内存中加载文件系统中每个文件和每个数据块的引用关系(文件、block、datanode之间的映射信息) 数据会定期保存到本地磁盘,但不保存block的位置信息而是由DataNode注册时上报和在运行时维护。
- NameNode文件操作
NameNode负责文件元数据的操作,DataNode负责处理文件内容的读写请求,数据流不经过NameNode,但是会询问它跟哪个DataNode联系。
- NameNode职责
全权管理数据块的复制,周期性的接受心跳和块的状态报告信息(包含该DataNode上所有数据块的列表)若接受到心跳信息,NN认为DN工作正常,如果在10分钟后还接受到不到DN的心跳,那么NN认为DN已经宕机,这时候NN准备要把DN上的数据块进行重新的复制。
块的状态报告包含了一个DN上所有数据块的列表,blocks report 每个1小时发送一次。
- NameNode容错机制
没有NameNode,HDFS就不能工作。事实上,如果运行NameNode的机器坏掉的话,系统中的文件将会完全丢失,因为没有其他方法能够将位于不同DataNode上的文件块(blocks)重建文件。因此,NameNode的容错机制非常重要,Hadoop提供了两种机制:
- 第一种方式是将持久化存储在本地硬盘的文件系统元数据备份。Hadoop可以通过配置来让NameNode将它的持久化状态文件写到不同的文件系统中。这种写操作是同步并且是原子化的。比较常见的配置是在将持久化状态写到本地硬盘的同时,也写入到一个远程挂载的网络文件系(NFS)。
- 第二种方式是运行一个辅助的NameNode(SecondaryNamenode)。 事实上SecondaryNamenode并不能被用作NameNode,它的主要作用是定期的将Namespace镜像与操作日志文件(edit log)合并,以防止操作日志文件(edit log)变得过大。通常,SecondaryNamenode 运行在一个单独的物理机上,因为合并操作需要占用大量的CPU时间以及和NameNode相当的内存。辅助NameNode保存着合并后的Namespace镜像的一个备份,万一哪天Namenode宕机了,这个备份就可以用上了。
但是辅助NameNode总是落后于主NameNode,所以在NameNode宕机时,数据丢失是不可避免的。在这种情况下,一般要结合第一种方式中提到的远程挂载的网络文件系统(NFS)中的NameNode的元数据文件来使用,把NFS中的NameNode元数据文件,拷贝到辅助NameNode,并把辅助NameNode作为主NameNode来运行。
- 文件系统元数据的持久化
- NameNode上保存着HDFS的名字空间。对于任何对文件系统元数据产生修改的操作,NameNode都会使用一种称为EditLog的事务日志记录下来。例如,在HDFS中创建一个文件,NameNode就会在Editlog中插入一条记录来表示;同样地,修改文件的副本系数也将往Editlog插入一条记录。NameNode在本地操作系统的文件系统中存储这个Editlog。整个文件系统的名字空间,包括数据块到文件的映射、文件的属性等,都存储在一个称为FsImage的文件中,这个文件也是放在NameNode所在的本地文件系统上。
- NameNode在内存中保存着整个文件系统的名字空间和文件数据块映射(Blockmap)的映像。这个关键的元数据结构设计得很紧凑,因而一个有4G内存的NameNode足够支撑大量的文件和目录。当NameNode启动时,它从硬盘中读取Editlog和FsImage,将所有Editlog中的事务作用在内存中的FsImage上,并将这个新版本的FsImage从内存中保存到本地磁盘上,然后删除旧的Editlog,因为这个旧的Editlog的事务都已经作用在FsImage上了。这个过程称为一个检查点(checkpoint)。在当前实现中,检查点只发生在NameNode启动时,在不久的将来将实现支持周期性的检查点。
- DataNode将HDFS数据以文件的形式存储在本地的文件系统中,它并不知道有关HDFS文件的信息。它把每个HDFS数据块存储在本地文件系统的一个单独的文件中。DataNode并不在同一个目录创建所有的文件,实际上,它用试探的方法来确定每个目录的最佳文件数目,并且在适当的时候创建子目录。在同一个目录中创建所有的本地文件并不是最优的选择,这是因为本地文件系统可能无法高效地在单个目录中支持大量的文件。当一个DataNode启动时,它会扫描本地文件系统,产生一个这些本地文件对应的所有HDFS数据块的列表,然后作为报告发送到NameNode,这个报告就是块状态报告。
1.2 DataNode
存储节点,真正存放数据的节点,用于保存数据,保存在磁盘上(在HDFS上保存的数据副本数默认是3个,这个副本数量是可以设置的)。基本单位是块(block),默认128M(2.x版本默认值)。
Block块的概念:
每台机器都有磁盘,机器上的所有持久化数据都是存储在磁盘上的。磁盘是通过块来管理数据的,一个块的数据是该磁盘一次能够读写的最小单位,一般是512个字节,而建立在磁盘之上的文件系统也有块的概念,通常是磁盘块的整数倍,例如几kb。HDFS作为文件系统,一样有块的概念,对于分布式文件系统,使用文件块将会带来这些好处:
- 一个文件的大小不限制于集群中任意机器的磁盘大小
- 因为块的大小是固定的,相对比不确定大小的文件,块更容易进行管理和计算
- 块同样方便进行备份操作,以提高数据容错性和系统的可靠性
为什么HDFS的块大小会比文件系统的块大那么多呢?
操作数据时,需要先从磁盘上找到指定的数据块然后进行传输,而这就包含两个动作:
1)数据块寻址:找到该数据块的起始位置
2)数据传输:读取数据也就是说,操作数据所花费的时间是由以上两个步骤一起决定的,步骤1所花费的时间一般比步骤2要少很多,那么当操作的数据块越多,寻址所花费的时间在总时间中就越小的可以忽略不计。所以块设置的大,可以最小化磁盘的寻址开销。但是HDFS的Block块也不能设置的太大,会影响到map任务的启动数,并行度降低,任务的执行数据将会变慢。
1.3 SecondaryNameNode
辅助节点,用于同步元数据信息。辅助NameNode对fsimage和edits进行合并(冷备份),下面用SNN代替NameNode 的元数据信息先往 edits 文件中写,当 edits 文件达到一定的阈值(3600 秒或大小到 64M)的时候,会开启合并的流程。合并流程如下:
- 当开始合并的时候,SNN 会把 edits 和 fsimage 拷贝到自己服务器所在内存中,开始合并,合并生成一个名为 fsimage.ckpt 的文件。
- 将 fsimage.ckpt 文件拷贝到 NameNode 上,成功后,再删除原有的 fsimage,并将 fsimage.ckpt文件重命名为 fsimage。
- 当 SNN 将 edits 和 fsimage 拷贝走之后,NameNode 会立刻生成一个 edits.new 文件,用于记录新来的元数据,当合并完成之后,原有的 edits 文件才会被删除,并将 edits.new 文件重命名为 edits 文件,开启下一轮流程。
二、HDFS读写流程
2.1 读流程
文件在 Hadoop 中的读取操作如下图所示:
读步骤解读:
- 客户端启动通过调用文件系统中
DistributedFileSystem
类型对象的open()
方法读取请求。- 此对象使用 RPC 连接到 NameNode 并获取的元数据信息,如该文件的块的位置。 请注意,这些地址是文件的前几个块。
- 响应该元数据请求,具有该块副本的 DataNodes 地址被返回。
- 一旦接收到 DataNodes 的地址,一个
FSDataInputStream
类型的对象被返回到客户端。FSDataInputStream
包含DFSInputStream
这需要处理交互 DataNode 和 NameNode。在上图所示的步骤4,客户端调用read()
方法,这将导致DFSInputStream
建立与第一个 DataNode 文件的第一个块连接。- 以数据流的形式读取数据,其中客户端多次调用
read()
方法。read()
操作这个过程一直持续,直到它到达块结束位置。- 一旦到模块的结尾,
DFSInputStream
关闭连接,移动定位到下一个 DataNode 的下一个块- 一旦客户端已读取完成后,它会调用
close()
方法。
2.2 写流程
在将文件数据写入到 HDFS中时,流程如下图:
写步骤详解:
- 客户端通过调用
DistributedFileSystem
对象的create()
方法创建一个新的文件,并开始写操作 - 对应上图中步骤1。DistributedFileSystem
对象使用 RPC 调用连接到 NameNode,并启动新的文件创建。但是,此文件创建操作不与文件任何块相关联。NameNode 的责任是验证文件(其正被创建的)不存在,并且客户端具有正确权限来创建新文件。如果文件已经存在,或者客户端不具有足够的权限来创建一个新的文件,则抛出IOException
到客户端。否则操作成功,并且该文件新的记录是由 NameNode 创建。- 一旦 NameNode 创建一条新的记录,返回一个
FSDataOutputStream
类型的对象到客户端。客户端使用它来写入数据到 HDFS。数据写入方法被调用(图中的步骤3)。FSDataOutputStream
对象包含DFSOutputStream
对象,它使用 DataNodes 和 NameNode 通信后查找。当客户机继续写入数据,DFSOutputStream
继续创建这个数据包。这些数据包连接排队到一个队列被称为 DataQueue- 还有一个名为 DataStreamer 组件,用于消耗DataQueue。DataStreamer 也要求 NameNode 分配新的块,拣选 DataNodes 用于复制。
- 现在,复制过程始于使用 DataNodes 创建一个管道。 在我们的例子中,选择了复制水平3,因此有 3 个 DataNodes 管道。
- 客户端把 packet 以流的方式写入第一个 datanode。
- 在每个 DataNode 的管道中存储数据包接收并同样转发在第二个 DataNode 的管道中。
- 另一个队列,“Ack Queue”是由
DFSOutputStream
保持存储,它们是 DataNodes 等待确认的数据包。- 一旦确认在队列中的分组从所有 DataNodes 已接收在管道,它从 ‘Ack Queue’ 删除。在任何 DataNode 发生故障时,从队列中的包重新用于操作。
- 在客户端的数据写入完成后,它会调用close()方法(第9步图中),调用close()结果进入到清理缓存剩余数据包到管道之后等待确认。
- 一旦收到最终确认,NameNode 连接告诉它该文件的写操作完成。
参考 https://www.yiibai.com/hadoop/hdfs_beginners_guide.html