10.1 HDFS文件结构

作为一名合格的系统运维人员,首先要全面掌握系统的文件组织目录。对于Hadoop系统的运维人员来说,就是要掌握HDFS中的NameNode、DataNode、Secondery NameNode是如何在磁盘上组织和存储持久化数据的。只有这样,当遇到问题时,管理人员才能借助系统本身的文件存储机制来快速诊断和分析问题。下面从HDFS的几个方面来分别介绍。

1.NameNode的文件结构

最新格式化的NameNode会创建以下目录结构:


${dfs.name.dir}/current/VERSION

/edits

/fsimage

/fstime


其中,dfs.name.dir属性是一个目录列表,是每个目录的镜像。VERSION文件是Java属性文件,其中包含运行HDFS的版本信息。下面是一个典型的VERSION文件包含的内容:


Wed Mar 23 16:03:27 CST 2011

namespaceID=1064465394

cTime=0

storageType=NAME_NODE

layoutVersion=-18


其中,namespaceID是文件系统的唯一标识符。在文件系统第一次被格式化时便会创建namespaceID。这个标识符也要求各DataNode节点和NameNode保持一致。NameNode会使用此标识符识别新的DataNode。DataNode只有在向NameNode注册后才会获得此namespaceID。cTime属性标记了NameNode存储空间创建的时间。对于新格式化的存储空间,虽然这里的cTime属性值为0,但是只要文件系统被更新,它就会更新到一个新的时间戳。storageType用于指出此存储目录包含一个NameNode的数据结构,在DataNode中它的属性值为DATA_NODE。

layoutVersion是一个负的整数,定义了HDFS持久数据结构的版本。注意,该版本号和Hadoop的发行版本号无关。每次HDFS的布局发生改变,该版本号就会递减(比如-18版本号之后是-19),在这种情况下,HDFS就需要更新升级,因为如果一个新的NameNode或DataNode还处在旧版本上,那么系统就无法正常运行,各节点的版本号要保持一致。

NameNode的存储目录包含edits、fsimage、fstime三个文件。它们都是二进制的文件,可以通过HadoopWritable对象进行序列化。下面将深入介绍NameNode的工作原理,以便使大家更清晰地理解这三个文件的作用。

2.编辑日志(edit log)及文件系统映像(filesystem image)

当客户端执行写操作时,NameNode会先在编辑日志中写下记录,并在内存中保存一个文件系统元数据,元数据会在编辑日志有所改动后进行更新。内存中的元数据用来提供读数据请求服务。

编辑日志会在每次成功操作之后、成功代码尚未返回给客户端之前进行刷新和同步。对于要写入多个目录的操作,写入流要刷新和同步到所有的副本,这就保证了操作不会因故障而丢失数据。

fsimage文件是文件系统元数据的持久性检查点。和编辑日志不同,它不会在每个文件系统的写操作后都进行更新,因为写出fsimage文件会非常慢(fsimage可能增长到GB大小)。这种设计并不会影响系统的恢复力,因为如果NameNode失败,那么元数据的最新状态可以通过将从磁盘中读出的fsimage文件加载到内存中来进行重建恢复,然后重新执行编辑日志中的操作。事实上,这也正是NameNode启动时要做的事情。一个fsimage文件包含以序列化格式存储的文件系统目录和文件inodes。每个inodes表示一个文件或目录的元数据信息,以及文件的副本数、修改和访问时间等信息。

正如上面所描述的,Hadoop文件系统会出现编辑日志不断增长的情况。尽管在NameNode运行期间不会对系统造成影响,但是,如果NameNode重新启动,它将会花费很长时间来运行编辑日志中的每个操作。在此期间(即安全模式时间),文件系统还是不可用的,通常来说这是不符合应用需求的。

为了解决这个问题,Hadoop在NameNode之外的节点上运行一个Secondary NameNode进程,它的任务就是为原NameNode内存中的文件系统元数据产生检查点。其实Secondary NameNode是一个辅助NameNode处理fsimage和编辑日志的节点,它从NameNode中复制fsimage和编辑日志到临时目录并定期合并生成一个新的fsimage,随后它会将新的fsimage上传到NameNode,这样,NameNode便可更新fsimage并删除原来的编辑日志。下面我们参照图10-1对检查点处理过程进行描述。

下面介绍检查点处理过程的具体步骤。

1)Secondary NameNode首先请求原NameNode进行edits的滚动,这样新的编辑操作就能够进入一个新的文件中了。

2)Secondary NameNode通过HTTP方式读取原NameNode中的fsimage及edits。

3)Secondary NameNode读取fsimage到内存中,然后执行edits中的每个操作,并创建一个新的统一的fsimage文件。

4)Secondary NameNode(通过HTTP方式)将新的fsimage发送到原NameNode。

5)原NameNode用新的fsimage替换旧的fsimage,旧的edits文件通过步骤1)中的edits进行替换。同时系统会更新fsimage文件到记录检查点记录的时间。

10.1 HDFS文件结构 - 图1

图 10-1 检查点处理过程

在这个过程结束后,NameNode就有了最新的fsimage文件和更小的edits文件。事实上,对于NameNode在安全模式下的这种情况,管理员可以通过以下命令运行这个过程:


hadoop dfsadmin-saveNamespace


这个过程清晰地表明了Secondary NameNode要有和原NameNode一样的内存需求的原因—要把fsimage加载到内存中,因此Secondary NameNode在集群中也需要有专用机器。

有关检查点的时间表由两个配置参数决定。Secondary NameNode每小时会插入一个检查点(fs.chec-kpoint.period,以秒为单位),如果编辑日志达到64MB(fs.checkpoint.size,以字节为单位),则间隔时间更短,每隔5分钟会检查一次。

3.Secondary NameNode的目录结构

Secondary NameNode在每次处理过程结束后都有一个检查点。这个检查点可以在一个子目录/previous.checkpoint中找到,可以作为NameNode的元数据备份源,目录如下:


${fs.checkpoint.dir}/current/VERSION

/edits

/fsimage

/fstime

/previous.checkpoint/VERSION

/edits

/fsimage

/fstime


以上这个目录和Secondary NameNode的/current目录结构是完全相同的。这样设计的目的是:万一整个NameNode发生故障,并且没有用于恢复的备份,甚至NFS中也没有备份,就可以直接从Secondary NameNode恢复。具体方式有两种,第一种是直接复制相关的目录到新的NameNode中。第二种是在启动NameNode守护进程时,Secondary NameNode可以使用-importCheckpoint选项,并作为新的NameNode继续运行任务。-importCheckpoint选项将加载fs.checkpoint.dir属性定义的目录中的最新检查点的NameNode数据,但这种操作只有在dfs.name.dir所指定的目录下没有元数据的情况下才进行,这样就避免了重写之前元数据的风险。

4.DataNode的目录结构

DataNode不需要进行格式化,它会在启动时自己创建存储目录,其中关键的文件和目录如下:


${dfs.data.dir}/current/VERSION

/blk_<id_1>

/blk_<id_1>.meta

/blk_<id_2>

/blk_<id_2>.meta

/……

/subdir0/

/subdir1/

/……

/subdir63/


DataNode的VERSION文件和NameNode的非常类似,内容如下:


Tue Mar 10 21:32:31 GMT 2010

namespaceID=134368441

storageID=DS-547717739-172.16.85.1-50010-1236720751627

cTime=0

storageType=DATA_NODE

layoutVersion=-18


其中,namespaceID、cTime和layoutVersion值与NameNode中的值都是一样的,namaspaceID在第一次连接NameNode时就会从中获取。stroageID相对于DataNode来说是唯一的,用于在NameNode处标识DataNode。storageType将这个目录标志为DataNode数据存储目录。

DataNode中current目录下的其他文件都有blk_refix前缀,它有两种类型:

1)HDFS中的文件块本身,存储的是原始文件内容;

2)块的元数据信息(使用.meta后缀标识)。一个文件块由存储的原始文件字节组成,元数据文件由一个包含版本和类型信息的头文件和一系列块的区域校验和组成。

当目录中存储的块数量增加到一定规模时,DataNode会创建一个新的目录,用于保存新的块及元数据。当目录中的块数量达到64(可由dfs.DataNode.numblocks属性值确定)时,便会新建一个子目录,这样就会形成一个更宽的文件树结构,避免了由于存储大量数据块而导致目录很深,使检索性能免受影响。通过这样的措施,数据节点可以确保每个目录中的文件块数是可控的,也避免了一个目录中存在过多文件。