对于生产集群,含有上千万文件,每次启动时间将会长达几十分钟,缩小启动时间将大大提高生产力。所以对启动时的各个环节进行分析并提出相应的解决方案用于减少启动时间。
1. NameNode启动中对fsimage加载过程解析
Hadoop在对NameNode进行启动时,首先会从映像文件(fsimage)中读取HDFS的状态,即系统目录树,同时将日志文件(edits)与fsimage进行合并,这样保证了内存中目录树是最新的,随后系统会将最新的目录树持久化到映像文件(fsimage)中,并使用一个空的edits文件开始正常操作。
因为NameNode只有在启动阶段才会合并fsimage和edits,所以久而久之edits文件将会十分庞大,尤其是对于大型的集群,这样将导致下一次NameNode启动会花很长时间。为此,secondary NameNode根据相应条件来合并fsimage和edits,在合并结束后也将最新的目录树持久化写入到fsimage。
对NameNode进行持久化存储的路径是由dfs.name.dir参数控制的。
dfs.name.dir, NameNode持久存储映像文件(fsimage)及日志文件(edits)的本地文件系统路径,当这个值是一个逗号分隔的目录列表时,系统实时目录树会被复制到所有目录中做冗余备份。
在Hadoop的版本中,持久化fsimage调用的函数为FSImage类中的saveFSImage()函数, 在函数内部首先读取dfs.name.dir中设置的多个目录列表,随后按照目录列表按顺序存储。
以下是NameNode加载fsimage过程的流程图:
2. 加载过程优化
如果NameSpace 存在大量文件,那么fsimage文件将会十分巨大,fsimage会达到上百兆甚至上G,如果在dfs.name.dir中定义了多个目录,那么采用按顺序存储势必会消耗一定时间。
为解决这一问题,对fsimage的持久化操作采用多线程技术,为目录列表中的每个目录存储开辟一个线程,用来存储fsimage文件。主线程等待所有存储的子线程完毕后完成对fsimage加载。这样,存储时间将取决于存储最慢的那个线程,达到了提高fsimage加载速度的目的,从而在一定程度上提升了NameNode启动速度。
其流程图如下:
3. 测试结果
测试采用利用hadoop 自带的nnbench,向NameNode写入大量小文件,写入文件总数为 200万 ;相关的命令为 hadoop jar hadoop-test.jar nnbench –operation create_write –maps 24 –bytesToWrite 1 –numberOfFiles 2000000 –baseDir /test/nnbench1。
目前测试集群有12个节点,总共200万文件,约200万块,dfs.name.dir设置了3个本地路径,其中两个本地路径用来模拟远程文件路径,fsimage大小为240M。
测试结果如下:
FSImage存储时间 顺序存储 多线程存储
第一次测试 22142msec 12709msec
第二次测试 17043msec 15813msec
第三次测试 20587msec 12286msec
平均时间 19924msec 13602msec
改动后时间缩短了1-13602/19924=32%
4. 结论
采用多线程写入fsimage,能够有效的提升fsimage加载速度,从而缩短NameNode启动速度。如果NameSpace存在大量文件,使得fsimage文件巨大,则这种时间缩短会更加明显。
这是我打的第一个patch,期望以后在hadoop业界继续努力。