【概述】
上一篇文章讲解了,在一定DN节点规模,一定block数据量下的并发写文件的优化问题。
在这种节点、数据量规模的集群中,当HDFS全部重启(nn dn全部重启),或者两个NN都重启后,需要经过较长时间,才能真正对外提供服务。
那么nn启动过程中都干了些啥,主要耗时点在哪,以及应当如何优化,本文就来聊聊这些问题——启动优化。
【启动流程与耗时分析】
NN的启动可以粗略的分为以下几个步骤:
- 启动http服务
- 加载文件系统
- 启动rpc服务
- 按需启动插件服务
- 处理dn的注册以及dn的全量块汇报
其中启动HTTP服务、RPC服务,都是在对应配置的IP端口上进行监听,然后创建对应的reader线程、handler线程,然后等待客户端的连接并处理客户端的请求。
而加载文件系统则是启动过程中的关键步骤,又可以细分为:
- 加载fsimage文件
- 加载fsimage的MD5文件并进行比对校验
- 加载editlog文件
- checkpoint的保存(可选)
- 进入安全模式
加载fsimage就是读取fsimage文件中的内容,并以此在内存中构建相关的元数据信息;加载editlog则是读取本地editlog文件或到JournalNode上读取editlog文件;checkpoint本质上还是对元数据信息持久化保存,对于HA模式而言,不需要进行checkpoint的保存处理;最后按需进入安全模式,等待dn的注册与块上报,当上报的块信息达到指定比例后(默认为99.9%),延迟一段时间后解除安全模式。
需要注意的是:这几个步骤是串行进行的,一个步骤完成后才进行下一个处理动作。
整个启动过程中,耗时的点在于加载文件系统和dn注册后的块汇报。加载文件系统的耗时在整个启动过程中占50%左右,剩下的50%为dn注册的块汇报处理。http服务和rpc服务的启动几乎不耗时。
【fsimage的格式】
既然加载文件系统占整个启动过程中一半的耗时,有必要先来了解了fsimage文件格式是怎样的,具体的加载过程又是怎样的。
整个fsimage文件由几个部分组成,最前面的是Header头信息,然后是多个section段,section之后是一个Summary概要信息,以及Summary的长度。
header的内容在2.4版本之后均固定为"HDFSIMG1",长度为8字节,同时指明后面的section的编码方式(采用protobuf的方式编码存储)。
summary则记录各个section的名字、在文件中的起始偏移位置以及长度。section则保存Hdfs元数据的相关信息,其中最重要的当属INODE和INODE_DIR两个section了,通常来说,这两个section占据fsimage的绝大部分空间。
INODE记录了hdfs中包含的所有文件/目录的信息,包括名称及可能的各个属性,每个INODE对应一条记录信息;而INDOE_DIR则记录了INODE的父子关系,也就是文件、目录的层级关系,同样也是一条记录对应一条关系信息。有了两个section,就可以构造出完整的文件系统目录树。
读取fsimage文件时,先读文件末尾4字节,得到Summary的起始位置,然后读取Summary的内容,这样就知道每个section的起始位置,长度,因此就可以按需加载各个section的内容了。
另外,section的加载顺序是串行的,即加载INODE_DIR时,必须先完成INODE的加载,否则可能出现找不到对应的inode条目。
【如何优化】
从上面fsimage的文件格式可以看出,每个文件的inode在INodeSection中都是一个独立的条目,读fsimage文件时单线程遍历每个条目,并在内存中构造对应的数据结构进行存储。因此,当有海量文件信息存储在fsiamge中时,单线程遍历必然是非常耗时的。这也就是启动耗时长的主要原因。
既然单线程加载很慢,那是否可以调整成多线程加载,每个线程读取其中的一部分,从而加速完成整个INodeSection的加载呢?
实际上,社区版本中的优化就是这么做的,将INodeSeciont拆分成多个带sub后缀的新分区名,同时保留原始的分区信息,同时在summary中增加各个子section的信息。
真正读取文件时,使用多线程进行加载,每个线程完成一部分子section的读取,实现读取的加速,同样INODE_DIR也通过多线程并行进行读取。
其中,拆分后的子分区个数由配置项("dfs.image.parallel.target.sections")决定,并行加载的线程数由配置项("dfs.image.parallel.threads")决定。
这样一来,就可以通过多线程并行进行加载INODE和INODE_DIR两个section,每个线程读取一个子section,有效提升加载速度。
除此之外,社区还有两个优化:
一个优化是:fsimage文件md5校验计算和fsimage的加载并行处理,在此之前是串行处理的,详见官方JIRA(HDFS-13694)。
另一个优化是:在加载INodeDirectorySection后,需要更新blocksMap、更新nameCache,然后将inode添加到inode directory的child列表中,优化的逻辑是将这三个操作步骤并行进行处理。
【优化效果】
从官方给出的优化效果来看,数据量在几个亿的级别时,三个优化整体能达到40%-60%的提升(有兴趣的可以去看看社区给出的测试数据)。
而实际测试发现,整体效果差不多是在这个范围内,但具体还会NN所在节点的CPU核数有一定的关系,并行加载时,CPU几乎处于满负荷状态,如果核数不够,那么效果会一般,核数稍微大一些,优化的效果会明显了。
【总结】
好了,小结一下,本文主要讲述了nn的启动流程,分析了主要的耗时点,然后结合fsimage的文件结构,讲述社区的实际优化处理,以及优化后的效果。