前言
看完本文的标题,可能有读者会心想:HDFS为什么会与小文件分析挂钩呢?Hadoop的设计初衷不是偏向于存储单位体量规模较大的文件的吗?设计这样的功能有什么实际用途呢?这背后其实是有很多内容可以讲的,说起HDFS中的小文件,我们并不是关心它到底有多小,而是在于它太多.而文件太多的原因往往在于其外部程序写入单个文件的量太小导致,在同样规模待写入数据量的前提下,单位文件过小显然会造成大批量的小文件产生.可能很多人会不以为然,认为小文件多了,但是它的总体数据量还是维持平稳,所以对HDFS影响就不大了.在这个分析过程中,我们往往会忽视小文件带来的间接影响:元数据规模的增多.这直接将会加重NameNode的负载.因为这会使得NameNode需要跟踪这些块并且要将这些文件块的元信息存放在自己的内存中.所以在这里引出了本文的主题:通过改造HDFS小文件分析功能,来处理集群中的小文件,然后达到最终减轻NameNode负载的目标.
背景知识
在讲解自定义小文件分析功能之前,我们有必要对现有HDFS的一些相关内容进行了解,主要包括以下4点:
- NameNode的元数据存放
- NameNode内存过大的影响
- 现有NameNode内存过大解决方案
- HDFS现有文件分析功能
NameNode的元数据存放
很多Hadoop的使用者在日常使用集群的时候往往对写入的文件缺乏管理,造成很多的小文件,有的甚至就是十几字节一个文件.这使HDFS完全被用成了小文件系统,从而难以发挥它本身的优势.这些庞大的元数据信息,我们如何能够查看到呢,近一步的说,我们如何能够查看到这些元数据信息占到NameNode内存的比例值呢?这里教给大家一个简单的方法,直接是java提供的jmap命令,命令如下:
jmap -histo:live <进程ID> | less //堆中活动的对象以及大小
此处进程ID填入NameNode进程ID即可.(大家如果要试的话,建议在Standby NameNode上执行,因为如果NameNode内存过大,jmap一次的时间会比较长,对其进程服务本身会造成影响).下面是我在测试集群上的执行结果:
num #instances #bytes class name
----------------------------------------------
1: xxxx xxxx org.apache.hadoop.hdfs.server.namenode.INodeFile
2: xxxx xxxx [Ljava.lang.Object;
3: xxxx xxxx org.apache.hadoop.hdfs.server.blockmanagement.BlockInfoContiguous
4: xxxx xxxx [B
5: xxxx xxxx [Lorg.apache.hadoop.hdfs.server.blockmanagement.BlockInfoContiguous;
6: xxxx xxxx [Lorg.apache.hadoop.util.LightWeightGSet$LinkedElement;
7: xxxx xxxx org.apache.hadoop.hdfs.server.namenode.INodeDirectory
8: xxxx xxxx java.util.ArrayList
9: xxxx xxxx org.apache.hadoop.hdfs.protocol.HdfsFileStatus
10: xxxx xxxx org.apache.hadoop.ipc.RetryCache$CacheEntryWithPayload
11: xxxx xxxx <constMethodKlass>
我们主要关注上面类对象的排序,具体的值大家可以自己在集群中做测试,基本是INodeFile是最多的,如果你有千万级别的文件在集群中,那么这个对象的instances值也将会达到千万级别.紧着着的是BlockInfoContiguous,这个对象类也非常的多,因为它要保存相邻副本块的位置等信息,具体的说就是它的内部会保存当前副本块的上一副本和下一副本的详细信息.当然我们在这里还看到了INodeDirectory对象,这个对应的就是目录的元信息了.最后在这里,我想纠正很多人可能对HDFS内存元数据的一个误解:
很多人可能就觉得在HDFS上新加一个文件,对于NameNode而言,就是增加几k文件元信息的事情,所以加个100w个什么的不会有很大问题,但是我们只看到了表象数据的增加,它潜在相关联对象的数据信息往往被我们忽略了,比如BlockInfoContiguous对象.
NameNode内存过大的影响
NameNode元数据过多会造成它的使用内存过大,而NameNode的内存过大将会很容易引发它的gc操作,从而进一步影响整个集群文件处理的响应速度.尤其在同时请求大规模文件量时,有时你可能会发现NameNode的响应速度非常的慢.
现有NameNode内存过大解决方案
如果NameNode内存已经达到了一个很大的值的情况下时,我们有什么现有的解决方案呢?答案是有的.如下:
用HDFS Federation来做,用多namespace来分担单个NameNode存储元数据的压力
而HDFS Federation的一个简单实现方案可以用ViewFs来做,viewFs相关内容可查看我的另外一篇文章