HDFS作为一个分布式文件系统,具有高容错的特点,它可以部署在廉价的通用硬件上,提供高吞吐率的数据访问,适合那些需要处理海量数据集的应用程序。HDFS没有遵循可移植操作系统接口(Portable Operating System Interface, POSIX)的要求,不支持"ls"或"cp"这样的标准UNIX命令,也不支持如fopen()和fread()这样的文件读写方法,而是提供了一套特有的、基于Hadoop抽象文件系统的API,支持以流的形式访问文件系统中的数据。
HDFS主要特性
HDFS的主要特性包括:
(1)、支持超大文件。超大文件在这里指的是几百MB、几百GB甚至几TB大小的文件,一般来说,一个Hadoop文件系统会存储T(1TB=1024GB)、P(1P=1024T)级别的数据。Hadoop需要能够支持这种级别的大文件。
检测和快速应对硬件故障。在大量通用硬件平台上构建集群时,故障,特别是硬件故障是常见的问题。一般的HDFS系统是由数百台甚至上千台存储着数据文件的服务器组成,这么多的服务器意味着高故障率。因此,故障检测和自动恢复是HDFS的一个设计目标。
(2)、流式数据访问。HDFS处理的数据规模都比较大,应用一次需要访问大量的数据。同时,这些应用一般是批量处理,而不是用户交互式处理。HDFS使应用程序能够以流的形式访问数据集,注重的是数据的吞吐量,而不是数据访问的速度。
(3)、简化的一致性模型。大部分的HDFS程序操作文件时需要一次写入,多次读取。在HDFS中,一个文件一旦经过创建、写入、关闭后,一般就不需要修改了。这样简单的一致性模型,有利于提供高吞吐量的数据访问模型。
正是由于以上的设计目标,HDFS并不适合如下应用:
(1)、低延迟数据访问。低延迟数据,如和用户进行交互的应用,需要数据在毫秒或秒的范围内得到响应。由于Hadoop针对高数据吞吐量做了优化,而牺牲了获取数据的延迟,对于低延迟访问,可以考虑使用HBase或Cassandra。
(2)、大量的小文件。HDFS支持超大文件,是通过将数据分布在数据节点(DataNode),并将文件的元数据保存在名字节点(NameNode)上。名字节点的内存大小,决定了HDFS文件系统可保存的文件数量,虽然现在的系统内存都比较大,但大量的小文件还是会影响名字节点的性能。
(3)、多用户写入文件、修改文件。HDFS中的文件只能有一个写入者,而且写操作总是在文件末。它不支持多个写入者,也不支持在数据写入后,在文件的任意位置进行修改。
总之,HDFS是为以流式数据访问模式存储超大文件而设计的文件系统,并在普通商用硬件集群上运行。
HDFS体系结构
为了支持流式数据访问和存储超大文件,HDFS引入了一些比较特殊的设计,在一个全配置的集群上,“运行HDFS”意味着在网络分布的不同服务器上运行一些守护进程(daemon),这些进程有各自的特殊角色,并相互配合,一起形成一个分布式文件系统。
HDFS采用了主从(Master/Slave)体系结构,名字节点NameNode、数据节点DataNode和客户端Client是HDFS中3个重要的角色。
在一个HDFS中,有一个名字节点和一个第二名字节点,典型的集群有几十到几百个数据节点,规模大的系统可以有上千、甚至几千个数据节点;而客户端,一般情况下,比数据节点的个数还多。名字节点和第二名字节点、数据节点和客户端的关系如图所示。
名字节点可以看作是分布式文件系统中的管理者,它负责管理文件系统命名空间、集群配置和数据块复制等。
数据节点是文件存储的基本单元,它以数据块的形式保存了HDFS中文件的内容和数据块的数据校验信息。
客户端和名字节点、数据节点通信,访问HDFS文件系统,操作文件。
1.数据块
在介绍上述各实体之前,首先了解一下HDFS中的重要概念:数据块(Block)。
在讨论文件系统的时候,特别是在分析文件系统的实现时,我们知道,为了便于管理,设备往往将存储空间组织成为具有一定结构的存储单位。如磁盘,文件是以块的形式存储在磁盘中,块的大小代表系统读/写操作的最小单位;在Linux的Ext3文件系统中,块大小默认为4096字节。文件系统通过一个块大小的整数倍的数据块,来使用磁盘。磁盘上的数据块管理属于文件系统实现的内部细节,对于通过系统调用读写文件的用户来说,是透明的。
HDFS也有块的概念,不过是更大的单元,默认HDFS数据块大小是64MB。和普通文件系统类似,HDFS上的文件也进行分块,块作为单独的存储单元,以Linux上普通文件的形式保存在数据节点的文件系统中。数据块是HDFS的文件存储处理的单元。
HDFS是针对大文件设计的分布式系统,使用数据块带来了很多的好处,具体如下:
1)HDFS可以保存比存储节点单一磁盘大的文件。
文件块可以保存在不同的磁盘上。其实,在HDFS中,文件数据可以存放在集群上的任何一个磁盘上,不需要保存在同一个磁盘上,或同一个机器的不同磁盘上。
2)简化了存储子系统。
简单化是所有系统的追求,特别是在故障种类繁多的分布式系统,将管理“块”和管理“文件”的功能区分开,简化了存储管理,也消除了分布式管理文件元数据的复杂性。
3)方便容错,有利于数据复制。
在HDFS中,为了应对损坏的块以及磁盘、机器故障,数据块会在不同的机器上进行复制(一般副本数为3,即一份数据保存在3个不同的地方),如果一个数据块副本丢失或者损坏了,系统会在其他地方读取副本,这个过程对用户来说是透明的,它实现了分布式系统中的位置透明性和故障透明性。同时,一个因损坏或机器故障而丢失的块会从其他地方复制到某一个正常运行的机器,以保证副本数目恢复到正常水平。该正常水平的副本数,也称副本系数。
HDFS的数据块比前面讨论过的磁盘块大得多,一个典型HDFS系统中,磁盘块的大小为64MB,也有使用128MB和256MB数据块大小的集群。为什么在HDFS中要使用这么大的数据块呢?原因和在磁盘上使用大磁盘块的原理是一样的。在普通文件系统中使用较大的磁盘块,可以减少管理数据块需要的开销,如在Linux中可以减少保存在i-node中磁盘地址表中的信息链的长度;同时,在对文件进行读写时,可以减少寻址开销,即磁盘定位数据块的次数。HDFS中使用大数据块,可以减少名字节点上管理文件和数据块关系的开销,同时,对数据块进行读写时,可以有效地减少建立网络连接需要的成本。
2.名字节点和第二名字节点
名字节点(NameNode)是HDFS主从结构中主节点上运行的主要进程,它指导主从结构中的从节点,数据节点(DataNode)执行底层的I/O任务。
名字节点HDFS维护着整个文件系统的文件目录树,文件/目录的元信息和文件的数据块索引,即每个文件对应的数据块列表(上述关系也称名字节点第一关系)。这些信息以两种形式存储在本地文件系统中:一种是命名空间镜像(File System Image, FSImage,也称文件系统镜像),另一种是命名空间镜像的编辑日志(Edit Log)。
命名空间镜像保存着某一特定时刻HDFS的目录树、元信息和数据块索引等信息,后续对这些信息的改动,则保存在编辑日志中,它们一起提供了一个完整的名字节点第一关系。
同时,通过名字节点,客户端还可以了解到数据块所在的数据节点信息。需要注意的是,名字节点中与数据节点相关的信息不保留在名字节点的本地文件系统中,也就是上面提到的命名空间镜像和编辑日志中,名字节点每次启动时,都会动态地重建这些信息,这些信息构成了名字节点第二关系。运行时,客户端通过名字节点获取上述信息,然后和数据节点进行交互,读写文件数据。
另外,名字节点还能获取HDFS整体运行状态的一些信息,如系统的可用空间、已经使用的空间、各数据节点的当前状态等。
第二名字节点(Secondary NameNode, SNN)是用于定期合并命名空间镜像和镜像编辑日志的辅助守护进程。和名字节点一样,每个集群都有一个第二名字节点,在大规模部署的条件下,一般第二名字节点也独自占用一台服务器。
第二名字节点和名字节点的区别在于它不接收或记录HDFS的任何实时变化,而只是根据集群配置的时间间隔,不停地获取HDFS某一个时间点的命名空间镜像和镜像的编辑日志,合并得到一个新的命名空间镜像。该新镜像会上传到名字节点,替换原有的命名空间镜像,并清空上述日志。应该说,第二名字节点配合名字节点,为名字节点上的名字节点第一关系提供了一个简单的检查点(Checkpoint)机制,并避免出现编辑日志过大,导致名字节点启动时间过长的问题。
如前面所述,名字节点是HDFS集群中的单一故障点,通过第二名字节点的检查点,可以减少停机的时间并减低名字节点元数据丢失的风险。但是,第二名字节点不支持名字节点的故障自动恢复,名字节点失效处理需要人工干预。
3.数据节点
HDFS集群上的从节点都会驻留一个数据节点的守护进程,来执行分布式文件系统中最忙碌的部分:将HDFS数据块写到Linux本地文件系统的实际文件中,或者从这些实际文件读取数据块。
虽然HDFS是为大文件设计,但存放在HDFS上的文件和传统文件系统类似,也是将文件分块,然后进行存储。但和传统文件系统不同,在数据节点上,HDFS文件块(也就是数据块)以Linux文件系统上的普通文件进行保存。客户端进行文件内容操作时,先由名字节点告知客户端每个数据块驻留在哪个数据节点,然后客户端直接与数据节点守护进程进行通信,处理与数据块对应的本地文件。同时,数据节点会和其他数据节点进行通信,复制数据块,保证数据的冗余性。
数据节点作为从节点,会不断地向名字节点报告。初始化时,每个数据节点将当前存储的数据块告知名字节点。后续数据节点工作过程中,数据节点仍会不断地更新名字节点,为之提供本地修改的相关信息,并接受来自名字节点的指令,创建、移动或者删除本地磁盘上的数据块。
下图说明了名字节点和数据节点的角色。
图中显示两个数据文件,它们都位于"usr/bin"目录下。其中,"data1.txt"文件有3个数据块,表示为b1、b2和b3,"data1.txt"文件由数据块b4和b5组成。这两个文件的内容分散在几个数据节点上。示例中,每个数据块都有3个副本。如数据块1(属于"data1.txt"文件)的3个副本分布于DataNode1、DataNode2和DataNode3上,当这些数据节点中任意一个崩溃或者无法通过网络访问时,可以通过其他节点访问"data1.txt"文件。
4.客户端
客户端是用户和HDFS进行交互的手段,HDFS提供了各种各样的客户端,包括命令行接口、Java API、Thrift接口、C语言库、用户空间文件系统(Filesystem in Userspace, FUSE)等。
虽然Hadoop不是一个POSIX文件系统,不支持"ls"和"cp"这样的命令,但Hadoop提供了一套和Linux文件命令类似的命令行工具,使熟悉Linux文件系统的用户,可以很快使用该工具,对HDFS进行操作。例如,下面的命令可以在HDFS文件系统上创建一个目录:
hadoop fs-mkdir testDIR
通过命令行工具,可以进行一些典型的文件操作,如读文件、创建文件路径、移动文件(包括文件改名)、删除文件、列出文件列表等,同时,命令行工具也提供了本地文件和HDFS交互的能力,可以通过下面的命令,将本地文件上传到HDFS:
hadoop fs-copyFromLocal testInput/hello.txt/user/alice/in/hello.txt
命令行工具提供了访问HDFS的基本能力,HDFS的Java API提供了更进一步的功能,目前,所有访问HDFS的接口都是基于Java API,包括上面介绍的命令行工具。HDFS的Java API实现了之前介绍的Hadoop抽象文件系统,包括DistributedFileSystem和对应的输入/输出流。
DistributedFileSystem继承自org.apache.hadoop.fs.FileSystem,实现了Hadoop文件系统界面,提供了处理HDFS文件和目录的相关事务;而DFSDataInputStream和DFSDataOutputStream分别实现了FSDataInputStream和FSDataOutputStream,提供了读写HDFS文件的输入/输出流。
下面是一个非常简单的HDFS Java API的例子:
Path inPath=new Path(“hdfs://<ip>:<port>/user/alice/in/hello.txt”);
FileSystem hdfs=FileSystem.get(inPath.toUri(),conf);
FSDataOutputStream fout=hdfs.create(inPath);
String data="testingtesting";
for(int ii=0;ii<256;ii++){
fout.write(data.getBytes());
}
fout.close();
FileStatus stat=hdfs.getFileStatus(inPath);
System.out.println("Replication number of file"+inPath
+"is"+stat.getReplication());
hdfs.delete(inPath);
在上面的代码中,通过FileSystem.get()获取的文件系统是DistributedFileSystem实例,而通过FileSystem.create()创建的输出流,则是一个DFSDataOutputStream实例。方法getFileStatus()和delete()用于获得HDFS上文件状态信息和删除文件,它们都是标准的Hadoop文件系统方法。
源码结构
HDFS的源代码分布在16个目录下,它们可以分为如下四类。
1.基础包
包括工具和安全包。其中,hdfs.util包含了一些HDFS实现需要的辅助数据结构;hdfs.security.token.block和hdfs.security.token.delegation结合Hadoop的安全框架,提供了安全访问HDFS的机制。
2.HDFS实体实现包
这是代码分析的重点,包含7个包:
hdfs. server.common包含了一些名字节点和数据节点共享的功能,如系统升级、存储空间信息等。
hdfs. protocol提供了HDFS各个实体间通过IPC交互的接口。
hdfs. server.namenode、hdfs.server.datanode和hdfs分别包含了名字节点、数据节点和客户端的实现。上述代码是HDFS代码分析的重点。
hdfs. server.namenode.metrics和hdfs.server.datanode.metrics实现了名字节点和数据节点上度量数据的收集功能。度量数据包括名字节点进程和数据节点进程上事件的计数,例如数据节点上就可以收集到写入字节数、被复制的块的数量等信息。
3.应用包
包括hdfs.tools和hdfs.server.balancer,这两个包提供查询HDFS状态信息工具dfsadmin、文件系统检查工具fsck和HDFS均衡器balancer(通过start-balancer.sh启动)的实现。
4.WebHDFS相关包
包括hdfs.web.resources、hdfs.server.namenode.web.resources、hdfs.server.datanode.web.resources和hdfs.web共4个包。
WebHDFS是HDFS 1.0中引入的新功能,它提供了一个完整的、通过HTTP访问HDFS的机制。对比只读的hftp文件系统,WebHDFS提供了HTTP上读写HDFS的能力,并在此基础上实现了访问HDFS的C客户端和用户空间文件系统(FUSE)。