一、概述

在 Namenode 中,命名空间(namespace,指文件系统中的目录树、文件元数据等信息)是被全部缓存在内存中的,且 Namenode 重启或者宕机,内存中的所有数据将会全部丢失,必须要有一种机制能够将整个命名空间持久化保存,并且能在Namenode 重启时重建命名空间。

Namenode 将命名空间信息记录在 fsimage(命名空间镜像)的二进制文件中,fsimage 将文件系统目录树中的每个文件或者目录的信息保存为一条记录,每条记录中包括了该文件(或目录)的名称、大小、用户、用户组、修改时间、创建时间等信息。Namenode 重启时,读取 fsimage 文件来重构命名空间。

1.1. 文件格式

**oiv ** 是 offline image viewer 的缩写,用于将fsimage文件的内容转储到指定文件中以便于阅读,该工具还提供了只读的WebHDFS API以允许离线分析和检查hadoop集群的命名空间。

oiv在处理非常大的fsimage文件时是相当快的,如果该工具不能够处理fsimage,它会直接退出。该工具不具备向后兼容性,比如使用hadoop-2.4版本的oiv不能处理hadoop-2.3版本的fsimage,只能使用hadoop-2.3版本的oiv。就像它的名称所提示的(offline),oiv不需要hadoop集群处于运行状态。

1
hdfs oiv -i fsimage_0000000000000000000 -p XML -o ~/fsimage.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0"?>
<fsimage>
<version>
<layoutVersion>-63</layoutVersion>
<onDiskVersion>1</onDiskVersion>
<oivRevision>0b8464d75227fcee2c6e7f2410377b3d53d3d5f8</oivRevision>
</version>
<NameSection>
<namespaceId>730830636</namespaceId>
<genstampV1>1000</genstampV1>
<genstampV2>1000</genstampV2>
<genstampV1Limit>0</genstampV1Limit>
<lastAllocatedBlockId>1073741824</lastAllocatedBlockId>
<txid>0</txid>
</NameSection>
<INodeSection>
<lastInodeId>16385</lastInodeId>
<numInodes>1</numInodes>
<inode>
<id>16385</id>
<type>DIRECTORY</type>
<name></name>
<mtime>0</mtime>
<permission>root:supergroup:0755</permission>
<nsquota>9223372036854775807</nsquota>
<dsquota>-1</dsquota>
</inode>
</INodeSection>
<INodeReferenceSection></INodeReferenceSection>
<INodeDirectorySection></INodeDirectorySection>
<FileUnderConstructionSection></FileUnderConstructionSection>
...
</fsimage>

Namenode 会定期将文件系统的命名空间(文件目录树、文件、目录元信息)保存到 fsimage 文件中,以防止 Namenode 掉电或者进程崩溃。但如果 Namenode 实时地将内存中的元数据同步到 fsimage 文件中,将会非常消耗资源且造成Namenode 运行缓慢。所以Namenode 会先将命名空间的修改操作保存在editlog文件中,然后定期合并 fsimage
和 editlog 文件。

FSImage 类主要实现了以下功能。

  1. 保存命名空间: 将当前时刻 Namenode 内存中的命名空间保存到 fsimage 文件中。
  2. 加载 fsimage 文件: 将磁盘上 fsimage 文件中保存的命名空间加载到Namenode 内存中,这个操作是保存命名空间操作的逆操作。
  3. 加载editlog 文件: Namenode 加载了 fsimage 文件后,内存中只包含了命名空间在保存 fsimage文件时的信息,Namenode还需要加载后续对命名空间的修改操作,即 editlog 文件中记录的内容。所以FSImage 类还提供了加载editlog 文件到 Namenode 内存中的功能。

3.2.1. 保存命名空间

FSImage 类最重要的功能之一就是将当前时刻 Namenode 的命名空间保存到fsimage文件

9080

FSNameSystem 会调用 FSImage.saveNamespace() 方法触发命名空间的保存操作,saveNamespace() 会调用 saveFSImageInAllDirs() 方法执行具体的保存逻辑。

Namenode 可以定义多个存储路径来保存 fsimage 文件,对于每一个存储路径,saveFSImageInAllDirs() 方法都会启动一个线程负责在这个路径上保存 fsimage 文件。同时,为了防止保存过程中出现错误,命名空间信息首先会被保存在一个fsimage.ckpt 文件中,当保存操作全部完成之后,才会将 fsimage.ckpt 重命名为 fsimage 文件。之后saveFSImageInAllDirs() 方法会清理 Namenode 元数据存储文件夹中过期的 editlog 文件和 fsimage 文件。

命名空间具体的保存操作是由 FSImageSaver 类承担的,FSImageSaver 是 FSImage 中的内部类,也是一个线程类,它的run()方法调用了 saveFSImage() 方法来保存 fsimage 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* Save the contents of the FS image to the file.
* 从 saveFSImage() 方法可以看到,生成 image 文件的时候,并不是直接将内存镜像 dump 到对应的 image 磁盘目录,
* 而是会产生一个以 fsimage.ckpt 开头的中间文件;
* 如:fsimage.ckpt_0000000000992806947,
* 然后生成对应的MD5校验文件,如:fsimage.ckpt_0000000000992806947.md5。
*
* 当多目录 image 文件全部完成了中间文件的生成,再调用 renameCheckpoint(...) 方法,
* 将所有目录的中间文件 rename 为最终的格式为 fsimage_0000000000992806947 的文件;
*/
void saveFSImage(SaveNamespaceContext context, StorageDirectory sd,
NameNodeFile dstType) throws IOException {
//checkpoint的目标偏移位置
long txid = context.getTxId();
//保存image文件
File newFile = NNStorage.getStorageFile(sd, NameNodeFile.IMAGE_NEW, txid);
//保存MD5文件
File dstFile = NNStorage.getStorageFile(sd, dstType, txid);

FSImageFormatProtobuf.Saver saver = new FSImageFormatProtobuf.Saver(context);
FSImageCompression compression = FSImageCompression.createCompression(conf);

//按照压缩配置,将img存入到以fsimage.ckpt开头的中间文件中
saver.save(newFile, compression);
//保存md5文件
MD5FileUtils.saveMD5File(dstFile, saver.getSavedDigest());
//此处保存,是为了做checkpoint的时候,能知道上一次checkpoint的位置
storage.setMostRecentCheckpointInfo(txid, Time.now());
}

saveFSImage() 方法构造了一个 FSImageFormatProtobuf.Saver 对象来保存命名空间,FSImageFormatProtobuf 是一个工具类,提供了以 protobuf 格式读取和写入 fsimage 文件的方法。

详细看这个~《Hadoop源码学习-Namenode 元数据管理-FSImage》