卷1:第8章 HDFS——Hadoop分布式文件系统

2018-02-24 15:55 更新

Hadoop分布式文件系统(HDFS)的设计主旨,在于对超大规模数据集提供可靠的存储功能,并对用户应用程序提供高带宽的输入输出数据流。在大型的集群里,上千台服务器均可直接参与到数据存储和应用程序任务执行。通过多服务器,分布式的存储和计算,计算资源的规模能够按照需要增长,并兼顾在各种规模上经济适用性。 本文主要描述了HDFS的架构,并以Yahoo!企业数据服务为例,介绍了如何使用HDFS系统管理高达4O PB规模的数据库的经验。
8.1 介绍

Hadoop采用MapReduce范式[DG04]进行设计,提供了一套分布式文件系统和框架,用以对超大规模的数据集进行分析和变换。HDFS的接口沿袭了Unix文件系统的设计模式,但在其基础上做出了改进,以提高在实际应用中的访问性能。

Hapoop所具有一个重要特点,就是把数据和运算分开,并将二者分布存放在数以千计的服务器主机上,应用程序计算以及相关数据都以并行方式处理。一个Hadoop集群可以仅仅通过增加普通服务器的方式,来扩展其运算、存储和I/O带宽的规模。Yahoo所使用的Hadoop集群组,共包含40,000台服务器,存储并处理多达40 PB(1 Petabytes = 1000000000000000 字节)的应用数据,其中最大的单个集群,使用多达4000个服务器。此外,在世界范围内,还有100多家其他的组织和机构表示,他们也使用Hadoop来进行数据存储和处理。

HDFS将文件系统元数据(File System Metadata)和应用数据(Application Data)分离存放。与其他种类的分布式文件系统类似,例如PVFS[CIRT00], Lustre2, 以及GFS[GGL03],HDFS将元数据存放在专用服务器上,该服务器称为“NameNode”(名称节点);应用数据被存放在其他的服务器上,这些服务器称为“DataNode”(数据节点)。在该分布式系统中,各个服务器之间均通过网络连接,确保节点之间可以通过基于TCP族的协议进行相互通信。HDFS不像Lustre或者PVFS,它并不依赖于数据保护机制(例如RAID)来确保数据的稳定性,而是像GFS那样,在多个DataNode节点上保存数据的多个副本,以此来确保数据的稳定。采用这样的策略,其好处不仅仅在于数据安全方面,在数据传输带宽方面,由于一个数据有多个副本,因此可以通过多线程访问倍速提高带宽(就像迅雷下载的原理一样——译者注),并且采用此种方式还可以提高从较近的服务器节点上获取数据的几率。
8.2 架构
8.2.1 NameNode(名称节点)
HDFS命名空间采用层次化(树状——译者注)的结构存放文件和目录。文件和目录用NameNode上的inodes表示。Inode记录了权限,修改和访问时间,命名空间,磁盘容量等属性。文件内容会被分成不同的“大块”(典型分块策略是每块128M,不过用户可以对每个文件的分块大小进行选择)。NameNode负责维护命名空间树以及与DataNode上文件分块的映射关系。目前采用的设计结构是,没一个集群只有一个NameNode,一个NameNode可以对应多个DataNode以及成千上万的HDFS客户端。一个DataNode可以同步执行多个应用任务。
8.2.2 映像和日志
Inode和定义metadata的系统文件块列表统称为Image(映像). NameNode将整个命名空间映像保存在RAM中。而映像的持久化记录则保存在NameNode的本地文件系统中,该持久化记录被称为Checkpoint(检查点)。NameNode还会记录HDFS中写入的操作,并将其存入一个记录文件,存放在本地文件系统中,这个记录文件被叫做Journal(日志)。存放块位置的副本不属于持久化检查点(persistent checkpoint)的一个部分。 每个客户端发起的事务都会被记录到日志里,然后日志文件会被刷新和同步,再发送回客户端。NameNode上的检查点文件(Checkpoint file)一旦生成,就不允许再修改。如果NameNode重启,在系统管理员的要求下,或者根据CheckpointNode的定义(下章介绍),可以生成一个新的文件记录checkpoint。 在启动过程中,NameNode会从checkpoint中初始化命名空间映像,然后根据日志重现所有的写入更改操作。在NameNode开始响应客户端之前,一个新的checkpoint和一个空的日志将被保存到存储目录当中。
为了提高持久性,系统会将checkpoint文件和日志的多个冗余备份存储到多个独立的本地卷以及远程NFS服务器上。之所以存储到独立卷标,是为了避免单个卷标失效后造成文件丢失;存储到远程服务器则是为了预防整个节点崩溃后造成所有本地文件丢失。如果NameNode遇到了错误,无法将日志信息写入到某个存储目录,那么系统就会自动将该有问题的目录排除到存储目录列表的范围之外。如果NameNode发现连一个可用的存储目录都找不到,则会执行自动关闭操作(节点失效)。
NameNode是一个多线程的系统应用,可以同时处理多个客户端的申请。不过,将事务存储到磁盘是一个较大的性能瓶颈,因为如果有一个线程正在存储中,其他线程都必须等待该线程完成其刷新和同步过程完成后,才能继续进行操作。为了优化这一过程,NameNode采用将多个事务批处理的方式,当某个NameNode线程初始化了一个刷新同步操作时,所有的事务会被一次性批处理,然后一起提交。其他的线程只需要检查他们的事务是否被存储了即可,而不需要再去提交刷新同步操作。
8.2.3 数据节点
DataNode上的每一个块(block)副本都由两个本地文件系统上的文件共同表示。其中一个文件包含了块(block)本身所需包含的数据,另一个文件则记录了该块的元数据,包括块所含数据大小和文件生成时间戳。数据文件的大小等于该块(block)的真实大小,而不是像传统的文件系统一样,需要用额外的存储空间凑成完整的块。因此,如果一个块里只需要一半的空间存储数据,那么就只需要在本地系统上分配半块的存储空间即可。
在启动过程中,每个DataNode通过“握手”的方式与另外一个NameNode节点连接。之所以采用“握手”方式,是为了验证DataNode的命名空间ID以及软件的版本号。如果一个节点的ID或者版本号不匹配,那么DataNode节点就会自动关闭。
命名空间ID是在文件系统实例格式化的时候就分配好的。命名空间ID被在集群内的所有节点上都有持久化存储。由于不同命名空间ID的节点无法加入到集群中,因此能够保证集群文件系统的统一性。一个DataNode在刚初始化的时候没有命名空间ID,此时该节点被允许加入集群,一旦加入,该节点就会以加入的集群的ID作为自己的ID。
“握手”之后,DataNode被注册到NameNode。DataNode持久化保存其唯一的存储ID(storage ID)。存储ID是一个DataNode的内部标识符,该标识符能够确保即使是服务器用不同的IP地址或者端口启动,仍然可以被识别。存储ID在DataNode首次注册到NameNode时即被分配,一旦分配后便无法更改。
DataNode采用发送“块报告”(block report)的形式,向NameNode标识其所包含的块副本。块报告包含了块ID,生成时间戳,以及每个块副本的长度等等。 首个块报告会在DataNode注册后立即发送。随后的块报告会每小时发送一次,以确保NameNode能够知道集群中块副本的最新情况。
在正常情况下,DataNode想NameNode发送“心跳信号”,以确认DataNode运行正常,以及其所包含的块数据可用。默认的“心跳信号”的时间间隔是3秒。如果NameNode长达10分钟没有接受到来自于DataNode的心跳信号,那么久会认为为该DataNode节点已经失效,其所包含的块(block)已经无法使用。接下来NameNode就会计划在其他的DataNode上创建新的块数据。
来自DataNode的心跳信号也会附带包括总存储容量,存储使用量,当前数据传输进度等附加信息。这些统计数据可用于NameNode块分配,以及作为负载均衡决策的参考。
NameNode不能直接向DataNode发送请求。它只通过回复心跳信号的方式来向DataNode发送指令。指令的内容包括,将块移到其他节点,移除本地块副本,重新注册和发送即时块报告,关闭当前节点等等。
这些命令对于维护整个系统的完整性来说非常关键,因此即使是在超大集群上,保持心跳信号的频率也是至关重要的。NameNode每秒能够处理上千条心跳信号,并且不影响到NameNode的其他正常操作。
8.2.4 HDFS客户端
用户应用程序通过HDFS客户端连接到HDFS文件系统,通过库文件可导出HDFS文件系统的接口。
像很多传统的文件系统一样,HDFS支持文件的读、写和删除操作,还支持对目录的创建和删除操作。用户通过带命名空间的路径对文件和目录进行引用。用户程序不需要知道文件系统的元数据和具体存储在哪个服务器上,也不需要关心一个块有多少个副本。
当一个应用程序读一个文件的时候,HDFS客户端首先向NameNode索要包含该文件的文件块的DataNode节点的列表。该列表会按照网络拓扑距离的远近进行排序。然后客户端会直接与相应的DataNode节点进行联系,要求传输所需的文件块。当客户端写一个文件的时候,它会首先要求NameNode选择一个DataNode,该DataNode需要包含所写入的文件的首个文件块。接下来,客户端会搭建一个从节点到节点的通信管道,用以进行数据传输。当第一个块被写入后,客户端会申新的DataNode,用以写入下一个块。此时,新的通信管线建立,客户端会通过管线写入更多的数据。每个文件块所写入的DataNode节点也许会完全不同。客户端,NameNode和DataNode之间的关系如图8.1所示。
图8.1 HDFS客户端创建新文件

与传统的文件系统不同的是,HDFS提供一个API用以暴露文件块的位置。这个功能允许应用程序,例如MapReduce框架,去数据所存放的地点进行任务调度,以此来提高读数据的新能。API也允许一个应用程序设定文件的复制因子。默认情况下,文件的复制因子是3,。对于关键的文件或者使用频率较多的文件,使用更高的复制因子,能够提高容错性,以及提升文件的访问带宽。
8.2.5 检查点节点
HDFS中的NameNode节点,除了其主要职责是相应客户端请求以外,还能够有选择地扮演一到两个其他的角色,例如做检查点节点或者备份节点。该角色是在节点启动的时候特有的。
检查点节点定期地域已经存在的检查点和日志一起,创建新的检查点和空日志。检查点节点通常运行于一个非NameNode节点的主机上,但它和NameNode节点拥有相同的内存需求配置。检查点节点从NameNode上下载当前的检查点和日志文件,将其在本地进行合并,并将新的检查点返回到NameNode.
创建一个定期检查点是保护文件系统元数据的一种方式。如果命名空间映像中的所有其他持久化拷贝均无法使用,系统还能够从最近一次的checkpoint文件中启动。当创建一个新的checkpoint被更新到NameNode的时候,还能让NameNode产生截断日志的效果。HDFS集群组可以长时间持续运行,无需重启,但这也导致了系统日志的大小会不断增长。当系统日志大到一定程度的时候,日志文件丢失或者损坏的几率就会增加。所以,一个日志太大的节点需要重启一下来对日志文件进行更新(截断)。对于一个较大的集群来说,平均处理一周的日志内容需要耗费一小时的时间。所以较好的频率是,每天创建一次新日志。

8.2.6 备份节点
HDFS的备份节点是最近在加入系统的一项特色功能。就像CheckpintNode一样,备份节点能够定期创建检查点,但是不同的是,备份节点一直保存在内存中,随着文件系统命名空间的映像更新和不断更新,并与NameNode的状态随时保持同步。
备份节点从活动的NameNode节点中接受命名空间事务的日志流,并将它们以日志的形式存储在其自身所带的存储目录里,并使用自身的内存和命名空间映像来执行这个事务。NameNodez则将BackupNode当做日志一样看待,就仿佛是存储在其自身的存储目录里一样。如果NameNode失效,那么BackupNode节点内存中的映像和磁盘上的checkpoint文件就可以作为最近的命名空间状态的备份,以备还原。
BackupNode能够动态创建一个checkpoint,而不需要从活动的NameNode上下载其checkpoint文件和日志文件。因为BackupNode始终把最新的状态保存在它自身命名空间的内存中。这一特性使得在BackupNode节点上处理checkpoint变得非常高效,因为只需要把命名空间存储到本地服务器就可以了,而不需要和NameNode再进行交互。
BackupNode还可以被看做是一个只读的NameNode. 它包含了除文件块位置以外的所有文件系统的元数据信息。除了修改命名空间或者文件块位置以外,BackupNode可以做NameNode所能做的所有操作。使用BackupNode能够使NameNode在运行的时候不进行持久化存储,从而把持久化命名空间状态的任务挪到BackupNode节点上进行。
8.2.7 系统更新和文件系统快照
在软件更新的过程中,由于软件的bug或者人为操作的失误,文件系统损坏的几率会随之提升。在HDFS中创建系统快照的目的,就在于把系统升级过程中可能对数据造成的隐患降到最低。
快照机制让系统管理员将当前系统状态持久化到文件系统中,这样以来,如果系统升级后出现了数据丢失或者损坏,便有机会进行回滚操作,将HDFS的命名空间和存储状态恢复到系统快照进行的时刻。
系统快照(只能有一个)在系统启动后,根据集群管理员的选择可随时生成。如果要求生成一个系统快照,NameNode首先会读取checkpoint和日志文件,并将其在内存中合并。然后,NameNode在新的位置创建并写入一个新的checkpoint和空的日志,保证旧的Checkpoint和日志不会被改变。
在进行“握手”方式通信时,NameNode指示DataNode是否要创建一个本地的系统快照。DataNode上的本地系统快照不会复制本地的存储目录中包含的数据信息,因为如果这么做的话,会使得每个集群上的DataNode上的数据占据双倍的存储空间。因此,取而代之的策略是,建立一份目录结构的副本,并用“链接”的方式将已经存在的块文件指向到目录副本。当DataNode尝试移除一个文件块时,只需要移除链接就可以了,在文件块增量时,也是使用copy-on-wirte技术。所以旧的块副本在其原先的目录结构中保持不变。
集群管理员可以选择在重启系统时,将HDFS回滚到快照所表示的状态。NameNode在快照恢复时,会恢复所记录的checkpoint。DataNode则会恢复之前被更名的文件目录,并初始化一个幕后进程,用以删除在snapshot创建之后系统新增的文件块副本。选择回滚后,回滚之前的操作将无法再继续(无法前滚)。集群管理员可以下达放弃快照的指令,来恢复被快照功能所占用的存储权。如果再系统升级期间进行快照,那么升级过程将被终止。
随着系统的升级,NameNode的checkpoint文件和日志文件的格式可能会发生变化,或者块文件的数据结构也可能发生改变。因此,系统中使用“设计版本号”(layout version)来标识数据表现格式,该版本号被持久化地存储在NameNode以及DataNode的存储目录中。每个节点在启动时,会将其系统设计版本号和存储目录中的设计版本号进行对比,并自动尝试将旧的数据格式转换到新的版本。为了实现系统版本升级,新版本重启时会强制创建系统快照。
8.3 文件 I/O 操作和复制管理
当然,一个文件系统的根本任务是用来存储数据和文件。如果要理解HDFS如何完成这一基本任务,我们就必须从它如何进行数据的读写,以及文件块如何管理这两方面来说明。
8.3.1 文件读写
应用程序通过创建新文件以及向新文件写数据的方式,给HDFS系统添加数据。文件关闭以后,被写入的数据就无法再修改或者删除,只有以“追加”方式重新打开文件后,才能再次为文件添加数据。HDFS采用单线程写,多线程读的模式。
HDFS客户端需要首先获得对文件操作的授权,然后才能对文件进行写操作。在此期间,其他的客户端都不能对该文件进行写操作。被授权的客户端通过向NameNode发送心跳信号来定期更新授权的状态。当文件关闭时,授权会被回收。文件授权期限分为软限制期和硬限制期两个等级。当处于软限制期内时,写文件的客户端独占对文件的访问权。当软限制过期后,如果客户端无法关闭文件,或没有释放对文件的授权,其他客户端即可以预定获取授权。当硬限制期过期后(一小时左右),如果此时客户端还没有更新(释放)授权,HDFS会认为原客户端已经退出,并自动终止文件的写行为,收回文件控制授权。文件的写控制授权并不会阻止其他客户端对文件进行读操作。因此一个文件可以有多个并行的客户端对其进行读取。
HDFS文件由多个文件块组成。当需要创建一个新文件块时,NameNode会生成唯一的块ID,分配块空间,以及决定将块和块的备份副本存储到哪些DataNode节点上。DataNode节点会形成一个管道,管道中DataNode节点的顺序能够确保从客户端到上一DataNode节点的总体网络距离最小。文件的则以有序包(sequence of packets)的形式被推送到管道。应用程序客户端创建第一个缓冲区,并向其中写入字节。第一个缓冲区被填满后(一般是64 KB大小),数据会被推送到管道。后续的包随时可以推送,并不需要等前一个包发送成功并发回通知(这被称为“未答复发送”——译者注)。不过,这种未答复发送包的数目会根据客户端所限定的“未答复包窗口”(outstanding packets windows)的大小进行限制。
在数据写入HDFS文件后,只要文件写操作没有关闭,HDFS就不保证数据在此期间对新增的客户端读操作可见。如果客户端用户程序需要确保对写入数据的可见性,可以显示地执行hflush操作。这样,当前的包就会被立即推送到管道,并且hflush操作会一直等到所有管道中的DataNode返回成功接收到数据的通知后才会停止。如此就可以保证所有在执行hflush之前所写入的数据对试图读取的客户端用户均可见。

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号