HDFS(Hadoop Distributed File System)是Hadoop项目的核心子项目,在大数据开发中通过分布式计算对海量数据进行存储与管理。它基于流数据模式访问和处理超大文件的需求而开发,可以运行在廉价的商用服务器上,为海量数据提供了不怕故障的存储方法,进而为超大数据集的应用处理带来了很多便利。
下面是HDFS的一些显著特征:
·HDFS非常适合使用商用硬件进行分布式存储和分布式处理。它具有容错性、可扩展性,并且扩展极其简单。
·HDFS具有高度可配置性。大多数情况下,需要仅针对非常大的集群调整默认配置。
·HDFS是Hadoop的核心框架,而Hadoop是用Java编写的,因此可以运行于所有主流平台上。
·支持类似Shell的命令直接与HDFS交互。
·HDFS内置了Web服务器,可以轻松检查集群的当前状态。
HDFS的主要设计目标如下。
硬件故障是常态,而非例外。HDFS集群可能由数百或数千台服务器组成,每台服务器都存储文件系统数据的一部分,且每台服务器都有很大的故障概率。因此,故障检测和快速、自动地从故障中恢复是HDFS的核心架构目标。
在HDFS上运行的应用程序需要对其数据集进行流式访问,这与运行在一般文件系统上的应用不同。HDFS更适合批量处理,而不是用户的交互使用,它更加强调数据访问的高吞吐量,而不是数据访问的低延迟。
HDFS是一个支持大型数据集的文件系统,一个典型的HDFS数据文件可以从千兆字节到兆兆字节不等,可以为具有数百个节点的集群提供高聚合的数据带宽和规模,同时承载千万个文件。
HDFS遵循简单一致性模型,一次写入,多次读取。即文件一旦被建立、写入、关闭,就不能被改变,更不能从文件任意位置进行修改,以此来保证数据的一致性。在Hadoop 2.x以后,支持在文件末尾追加内容。
HDFS被设计为能更好地将计算迁移到更接近数据的位置,而不是将数据移动到应用程序运行的位置,这样应用程序的计算效率会更高。HDFS为应用程序提供接口,使其更接近数据所在的位置。
HDFS被设计为可以从一个平台轻松地移植到另一个平台,从而使HDFS能够作为大型应用程序的首选平台。
HDFS是使用Java语言构建的系统,任何支持Java的计算机都可以运行HDFS,HDFS集群的总体架构如图4-1所示。
图4-1 HDFS集群的总体架构
HDFS是一个典型的主/从架构的分布式系统。一个HDFS集群由一个元数据节点(称为NameNode)和一些数据节点(称为DataNode)组成。NameNode是HDFS的主节点,是一个用来管理文件元数据(文件名称、大小、存储位置等)和处理来自客户端的文件访问请求的主服务器;DataNode是HDFS的从节点,用来管理对应节点的数据存储,即实际文件数据存储于DataNode上,而DataNode中的数据则保存在本地磁盘。
当HDFS系统启动后,NameNode上会启动一个名为“NameNode”的进程,DataNode上会启动一个名为“DataNode”的进程。
典型的HDFS集群部署是有一台专用的计算机只运行NameNode进程,集群中的其他计算机都运行DataNode进程。HDFS架构并不排除在同一台计算机上运行多个DataNode进程,但在实际部署中很少出现这种情况。
HDFS集群中单个NameNode的存在极大地简化了系统的体系结构。NameNode是HDFS元数据的仲裁者和存储库。该系统的设计方式是,用户数据永远不会流经NameNode。
此外,还有一个重要组件为SecondaryNameNode,4.1.3节将对HDFS各组件进行详细讲解。
HDFS系统的主要构成组件如下。
HDFS中的文件是以数据块(Block)的形式存储的,默认最基本的存储单位是128MB(Hadoop 1.x为64MB)的数据块。也就是说,存储在HDFS中的文件都会被分割成128MB一块的数据块进行存储,如果文件本身小于一个数据块的大小,则按实际大小存储,并不占用整个数据块空间。
HDFS的数据块之所以会设置这么大,其目的是减少寻址开销。数据块数量越多,寻址数据块所耗的时间就越多。当然也不会设置过大,MapReduce中的Map任务通常一次只处理一个块中的数据,如果任务数太少,作业的运行速度就会比较慢(MapReduce将在第5章讲解)。
HDFS的每一个数据块默认都有三个副本,分别存储在不同的DataNode上,以实现容错功能。因此,若数据块的某个副本丢失并不会影响对数据块的访问。数据块大小和副本数量可在配置文件中更改。HDFS数据块的存储结构如图4-2所示。
图4-2 HDFS数据块的存储结构
NameNode是HDFS中存储元数据(文件名称、大小和位置等信息)的地方,它将所有文件和文件夹的元数据保存在一个文件系统目录树中,任何元数据信息的改变,NameNode都会记录。HDFS中的每个文件都被拆分为多个数据块存放,这种文件与数据块的对应关系也存储在文件系统目录树中,由NameNode维护。
NameNode还存储数据块到DataNode的映射信息,这种映射信息包括:数据块存放在哪些DataNode上、每个DataNode上保存了哪些数据块。
NameNode也会周期性地接收来自集群中DataNode的“心跳”和“块报告”。通过“心跳”与DataNode保持通信,监控DataNode的状态(活着还是宕机),若长时间接收不到“心跳”信息,NameNode会认为DataNode已经宕机,从而做出相应的调整策略。“块报告”包含了DataNode上所有数据块的列表信息。
DataNode是HDFS中真正存储数据的地方。客户端可以向DataNode请求写入或读取数据块,DataNode还在来自NameNode的指令下执行块的创建、删除和复制,并且周期性地向NameNode汇报数据块信息。
SecondaryNameNode用于帮助NameNode管理元数据,从而使NameNode能够快速、高效地工作。它并不是第二个NameNode,仅是NameNode的一个辅助工具。
HDFS的元数据信息主要存储于两个文件中:fsimage和edits。fsimage是文件系统映射文件,主要存储文件元数据信息,其中包含文件系统所有目录、文件信息以及数据块的索引;edits是HDFS操作日志文件,HDFS对文件系统的修改日志会存储到该文件中。
当NameNode启动时,会从文件fsimage中读取HDFS的状态,也会对文件fsimage和edits进行合并,得到完整的元数据信息,随后会将新HDFS状态写入fsimage。但是在繁忙的集群中,edits文件会随着时间的推移变得非常大,这就导致NameNode下一次启动的时间会非常长。为了解决这个问题,则产生了SecondaryNameNode,SecondaryNameNode会定期协助NameNode合并fsimage和edits文件,并使edits文件的大小保持在一定的限制内。SecondaryNameNode通常与NameNode运行在不同的计算机上,因为它的内存需求与NameNode相同,这样可以减轻NameNode所在计算机的压力。
SecondaryNameNode的工作流程如图4-3所示。
(1)当SecondaryNameNode准备从NameNode上获取元数据时,会通知NameNode暂停对文件edits的写入。NameNode收到通知后,会停止写入edits文件,而将新的操作日志信息写入到一个新的文件edits.new中。
(2)SecondaryNameNode通过http get方式将NameNode的元数据文件edits和fsimage获取到本地,并将其合并为一个新的文件fsimage.ckpt。
(3)SecondaryNameNode把新的文件fsimage.ckpt通过http post方式发回NameNode。NameNode收到SecondaryNameNode发回的新文件fsimage.ckpt后,用该文件覆盖掉原来的fsimage文件,并删除原有的edits文件,同时将文件edits.new重命名为edits,将文件fsimage.ckpt重命名为fsimage。
上述操作避免了NameNode日志的无限增长,从而加速了NameNode的启动过程。
图4-3 SecondaryNameNode的工作流程
如图4-4所示,当客户端需要读取文件时,首先向NameNode发起读请求,NameNode收到请求后,会将请求文件的数据块在DataNode中的具体位置(元数据信息)返回给客户端,客户端根据文件数据块的位置,直接找到相应的DataNode发起读请求。
图4-4 HDFS读数据流程
如图4-5所示,当客户端需要写文件时,首先向NameNode发起写请求,将需要写入的文件名、文件大小等信息告诉NameNode。NameNode会将文件信息记录到本地,同时会验证客户端的写入权限,若验证通过,会向客户端返回文件数据块能够存放在DataNode上的存储位置信息,然后客户端直接向DataNode的相应位置写入数据块。被写入数据块的DataNode也会将数据块备份到其他DataNode上。
图4-5 HDFS写数据流程
举个例子,我们可以把NameNode想象成是一个仓库管理员,管理仓库中的商品;把DataNode想象成是一个仓库,用于存储商品,而商品就是我们所说的数据。仓库管理员只有一个,而仓库可以有多个。当需要从仓库中获取商品时,需要先询问仓库管理员,获得仓库管理员的同意,并且得到商品所在仓库的具体位置(例如,在1号仓库的1号货架上),然后根据位置信息直接去对应的仓库中的相应货架上取得商品。当需要向仓库中存入商品时,同样需要先询问仓库管理员,获得仓库管理员的同意,并且得到仓库管理员提供的商品能够存放的具体位置,然后根据位置信息直接去对应的仓库中的相应货架上存入商品。
此外,用户可以使用多种客户端接口对HDFS发起读/写操作,包括命令行接口、代码API接口和浏览器接口,使用非常方便,而不需要考虑HDFS的内部实现。本章后续将详细讲解HDFS各种接口的使用。