HDFS 的设计主要基于以下六点考虑:
(1)容错
独立计算机的硬件错误不能当异常情况处理,而属于正常状态。HDFS 文件系统中会有许多个普通计算机节点构成, 在任何时间任何一个节点都有可能出现故障, 因此HDFS 应该设计成能够自动恢复和快速检测错误, 这应该是维持HDFS 可靠运行的核心目标。
(2)流式访问数据集
HDFS上运行的应用程序需要以流式访问所存储的数据集。这些应用程序都采用并行的批处理方式进行数据计算,不同于普通系统上用于数据处理的应用程序。提高数据访问吞吐量是研究HDFS的重点,响应时间和数据访问的延迟则不作过多考虑。
(3)大数据存储
HDFS 最基本的目标就是支持大数据存储。一个存储在HDFS 系统上面的普通文件大小都在千兆至T 字节, 一个HDFS 应用最基本的要求是能支撑海量文件。
(4) 数据一致性
HDFS 应用处理文件的方式是一次写入多次读取。单个文件写入到HDFS 中后就不需要改变。这种处理文件的方式让数据一致性问题得到简化, 能够大幅度的提高HDFS 文件访问的吞吐量。
(5)速度
移动计算的方式代价比移动数据的方式开销要低。一个需要计算的请求,如果计算离操作数据越近那么计算出来的结果就越高效, 特别是在海量级别的数据计算时,效率更加明显。
(6)可移植性
在异构的软件和硬件平台间提供可移植性。HDFS 由Java 语言开发,Java 语言的跨平台特性完美的解决了这一要求。
HDFS采用master/slave架构。一个HDFS集群是由一个Namenode和一定数目的Datanode组成,这些Datanode定时和Namenode通信,像Namenode反馈状态以及接受Namenode的指令[p。为了减轻Namenode的负担,Namenode上并不需要永久保存所有Datanode上包含有哪些数据块的信息,而是通过Datanode在启动时的上报数据块信息,来更新Namenode上的映射表。HDFS暴露了文件系统的名字空间,用户可以通过以文件的形式在上面存储数据。从内部看,一个文件其实被分成一个或多个数据块(至少需要被划分成一个块),这些块通常存储在多个Datanode上,通过冗余性来保证可靠性以及加快后期的读取速度。Datanode负责处理分布式文件系统客户端的实际的读写数据请求。在Namenode的统一调度下进行数据块的创建、删除和复制。
HDF S是一个能在大集群中存储超大文件的系统,支持跨机器存储,而且运行可靠。每个文件都会被分成一系列大小为64MB(初始定义为64,实际在工程中可以自己设置,报告的后面会有相关操作)的数据块,最后一个数据块除外。为了容错,在文件的数据块存储到DataNode中时相应的都会存储副本。一般情况下都会有三个副本并且放在不同的地方,第一个副本会被放在本地节点,同时本地机架上的另外一个节点会放置第二个副本,第三个副本则经复制后分别放到不同机架上的节点。这种方式的好处就是可以减少机架内的写流量,提高了写的性能,同时,可以自由配置每个文件的数据块大小和副本系数,应用程序可以指定某个文件的副本数目。副本系数可以在文件创建之前制定或者创建之后修改。
Namenode是整个分布式文件系统的主服务器,是整个系统的核心,从命名我们可以清晰的看出是整个系统的名称服务器,Namenode作为HDFS中文件目录和文件分配的管理者,它保存的最重要信息,就是下面两个映射:文件名到数据块;数据块到Datanode列表。其中文件名到数据块的信息保存在磁盘上(持久化);但Namenode上不保存数据块到Datanode列表。在保存文件名到数据块的过程中,为了保证每次修改不需要从新保存整个结构,HDFS使用操作日志来保存更新。现在可以得到Namenode需要存储在Disk上的信息了,包括:in_use.lock,fsimage和edits.in_use.lock的功能和Datanode的一致。fsimage保存的是文件系统的目录树,edits则是文件树上的操作日志,fstime是上一次新打开一个操作日志的时间(long型)。除此之外,Namenode执行文件系统的名字空间操作,比如打开、关闭、重命名文件或目录。它也负责确定数据块到具体Datanode节点的映射。NAMENODE主要功能
(1)管理元数据和文件块
管理元数据就是对它包含的名字空问、文件到文件块的映射、文件块到数据节点的映射进行管理。其中,要求文件名到数据块的映射不仅要保留在内存中,还要持久化到磁盘上。文件块的管理不仅包括文件块的新建、复制,同时还包括无效文件块的移除以及孤立文件块的回收等。
(2)简化元数据更新操作
为了避免每次都要重新保存整个结构,NameNode会记录每次对文件系统的元数据进行修改的操作,并用事务日志(Editlog)进行表示。与此类似,修改文件的副本系数的操作也会被记录到Editlog中,而Editlog会被NameNode存储到本地的操作系统的文件系统中。NameNode管理整个存储在FsImage文件中的文件系统的命名空间,包括文件的属性、数据块到文件的映射,FsImage文件与Editlog一样都被存储在NameNode所在的本地文件系统中。当NameNode启动时,Editlog和FsImage就从硬盘中被其读取,把Editlog记录的所有事务作用于FsImage,并将新产生的Fslmage替代旧的Editlog刷新到硬盘中。
(3)监听和处理请求
与客户端和DataNode不同,NameNode只是监听客户端事件及DataNode事件。而不会主动发起请求,客户端事件通常包括目录和文件的创建、读写、重命名和删除,以及文件列表信息获取等。DataNode事件主要包括数据块信息的汇报、心跳响应、出错信息等。当NameNode监听到这些请求时便对他们响应,并将相应的处理结果返回到请求端。
(4)心跳检测
连接建立以后,DataNode和NameNode之间会不断保持心跳,这样DataNode在向NameNode汇报自己的负载情况的同时还会接收来自NameNode的指令信息。如果有哪个DataNode没有在一定时间内对NameNode定期的ping作出回应,就会被认为是出现了故障,此时,NameNode就会重新调整整个文件系统。
Datanode负责存储数据,一个数据块在多个Datanode中有备份,而一个Datanode对于一个数据块最多只包含一个备份。所以可以简单地认为Datanode上存了数据块ID和数据块内容,以及他们的映射关系。一个HDFS集群可能包含上千Datanode节点,这些Datanode定时和Namenode通信,接受Namenode的指令。为了减轻Namenode的负担,Namenode上并不永久保存那个 Datanode上有那些数据块的信息,而是通过Datanode启动时的上报,来更新Namenode上的映射表。
Datanode和Namenode建立连接以后,就会不断地和Namenode保持心跳。心跳返回时包含了Namenode对Datanode的一些命令,如删除某个数据块或者是把数据块从一个D atanode复制到另一个D atanode。应该注意的是:Namenode不会发起到Datanode的请求,在这个通信过程中,它们是严格的客户端/服务器架构。Datanode当然也作为服务器接受来自客户端的访问,处理数据块读/写请求。Datanode之间还会相互通信,执行数据块复制任务,同时,在客户端做写操作的时候,Datanode需要相互配合,保证写操作的一致性。
DATANODE主要功能:
(1) 数据块的读写。
所有文件的数据块都存储在DataNode中,但客户端并不知道某个数据块具体的位置信息,所以不能直接通过DataNode进行数据块的相关操作,所有这些位置信息都存储在NameNode。因此,当系统客户端需要执行数据块的创建、复制和删除等操作时,需要首先访问NameNode以获取数据块的位置信息,然后再访问指定的DataNode来执行相关操作,具体的文件操作最终由客户端进程而非DataNode来完成。
(2) 向NameNode报告状态。
由于数据块到DataNode的映射并没有持久化到NameNode本地磁盘,NameNode只能通过与DataNode之间的心跳信号来掌握文件块状态,进而掌握整个工作集群中所有DataNode节点状态的整体布局,假如DataNode出现异常状态,及时作出调整。当某个DataNode出现故障而导致失效时,为了保证数据块的副本数量达到规定范围。NameNode就会调度相关DataNode执行失效结点上数据块的复制操作。
(3) 执行数据的流水线复制。
当创建文件或者是某些DataNode出现故障而导致数据块副本数量少于规定数目时,系统要执行数据块复制操作。客户端在从NameNode获取到需要进行复制的数据块列表之后,首先会把缓存于客户端的数据块复制到第一个DataNode上,然后采取流水线复制的方式,在同一时间将第一个DataNode上的数据复制到第二个DataNode,以此类推,直到所有数据块的复制操作全部完成。流水线复制有效地提升了系统的运行速度,特别是针对某些热点数据需要复制大量Block的情形,往往能取得非常好的效果。
HDFS元数据缓存策略:
HDFS的所有元数据都是由Namenode来进行管理和维护的。在Namenode中一共保存了以下3种类型的元数据:文件和块的命名空间、文件名到块的映射、以及每个块副本的位置。其中文件名到数据块的映射持久化在磁盘土,而不仅仅保留在内存中。所有对目录树的更新以及文件名和数据块之间关系的修改,都必须能够持久化。为了保证每次修改不需要重新保存整个结构,HDFS使用操作日志来保存更新。对于任何对文件元数据产生的修改,Namenode都会使用一个称为Editlog的事务日志记录下来。例如,在HDFS中删除一个文件,Namenode就会在Editlog中插入一条记录来表示;同样,修改文件的复制(replication因子也将在Editlog插入一条记录。Namenode在本地操作系统的文件系统中存储这个Editlog。整个文件系统的namespace,包括数据块到文件的映射、文件的属性,都存储在称为FsImage的文件中,这个文件也是放在Namenode所在操作系统的文件系统上。
Namenode在内存中保存着整个文件系统的名字空间和文件名到数据块的映射。这个关键的元数据设计得很紧凑,在存储大文件时,一个带有4G内存的Namenode足够支撑海量的文件和目录。当Namenode启动时,它从硬盘中读取Editlog和FsImage,将所有Editlog中的事务作用在内存中的FsImage,并将这个新的FsImage从内存中刷新到硬盘上,然后再丢弃这个旧的Editlog。
数据完整性策略:
由于网络的复杂性,可能导致DataNode 节点读取的数据为损坏状态,原因有多种可能性,常见的有网络通信错误、程序问题、DataNode 节点自身存储等等情况。这时就需要一套数据校验机制保证数据的完整性。客户端写入一个新的文件时,将会计算每个文件块的校验和, 然后将校验和保存为一个单独的隐藏文件, 并将校验和文件同时存储在目录空间下。如果客户端要读取文件,将会同时读取目录空间下的校验文件,并对DataNode 节点上的文件块的校验进行匹配, 如果匹配出现错误,那么说明该文件块的数据有问题,客户端将会选择其他的文件块副本所在的DataNode 节点进行文件块读取操作。
副本放置策略:
在HDFS中,每一个数据块默认条件下都会保存3个备份(也可以自行修改备份的数量),为了使后期读取效率更高并且同时保证数据的可靠性,HDFS设计了相应的副本放置策略,一般情况下,第一个block副本放在和client所在的node甩(如果client不在集群范围内,则这第一个node是随机选取的,当然系统会尝试不选择哪些太满或者太忙的node)。第二个副本放置在与第一个节点相同的机架中的node中(随机选择)。第三个副本与第一二个副本不同的机架中,随机放在不同的node中。如果还有更多的副本就随机放在集群的任意位置。HDFS的副本放置策略在充分考虑了系统的可靠性(block在不同的机架)和带宽(一个管道只需要穿越一个网络节点),并在它们之间做了一个很好的平衡。