背景
hadoop的HDFS系统结
构里,namenode一直是一个单点,不管是单点出错还是单点性能方便,都是单点。这一直是HDFS想要达到7 *
24小时服务的最大的阻碍。在hadoop
apache社区和仅有的那几家有能力把hadoop用到这种程度的人群里,对这一点的讨论也已经有很多了,有提出分布式namespace的,有提出
namenode单点热备的,有提出分布式mds(参考ceph和lustre)的,大家都为解决namenode的单点想了很多的办法。最近跟
facebook的Dhruba
Borthakur(这位仁兄的名字实在是不会念,只好大家都叫他DB同学)讨论中发现,他们的hdfs也碰到了相同的问题,facebook目前拥有全
球最大的hadoop集群,其中就有超过1200个slave节点,存储容量到12PB的HDFS集群,当集群储存的文件越来越多,block越来越多时,namenode单点的瓶颈就越来越明显。暂且不提由于单点的原因造成对namenode rpc调用带来的瓶颈(这一点得用更多的篇幅来记录了,相关测试数据和性能瓶颈分析以后再发好了),光就availability而言,每次集群修改了代码需要升级,或者例行升级,或者发生故障hdfs需要重启的时候,这个问题就凸现出来。
熟悉namenode内部程序和逻辑的同仁们都知道(呵呵,我说的就是你们,你们懂的),namenode重启时主要耗时的有两个地方:
- 对 fsimage的加载,这个中间还包括对 editlog文件,edits.new文件的加载,然后和先前加载的fsimage做merge,最后save fsimage到磁盘的时间。这其中还不排除有secondarynamenode挂掉导致edits log文件变得奇大无比(我碰到的最大的居然有18G!),导致加载fsimage没多久,而load和merge editlog却需要花费几个小时的情况……(提到这个不得不说,以前还真是没有经验,遇到这种情况的时候居然不知道是因为 secondarynamenode挂掉导致的,还以为在上次checkpoint之后对hdfs的操作频繁到能够写18G editlog的程度……)。
- 另 外一个大头就是接收所有datanode通过 rpc发送过来的blockReport,这个才是namenode启动时最耗时的地方。因为namenode本身并没有持久化block和 datanode对应的mapping信息,所以namenode里最耗内存的blockMap的结构在启动时需要初始化就必须接收datanode的 blockReport,这个地方就是最耗时,也是最令人头疼,也是yahoo(非中国)和facebook,以及我司(就是“我们公司”的意思,这个词是从一位有着”活百科全书”的神一样的男子那引用来的)的同仁们讨论的最多,想象空间最大,改造和优化空间最大的地方。
数据
这里有一组测试数据:
节点数 | 存储容量 | 文件和目录数 | fsimage加载时间 | blockReport时间 |
1200 | 12PB | 7000 万 | 10分钟左右 | 35分钟左右 |
650 | 7PB | 4500 万 | 6分钟左右 | 30分钟左右 |
由于测试数据和集群环境并非来自同一个地方,所有稍微有一些出入,但是总体能够看出,基本上影响HDFS 7 * 24 服务,High availability的瓶颈,就在这两个地方了。
解决方案
要解决HDFS HA问题,就必须解决namenode重启加载耗时过长的问题,一旦每次升级或者重启hdfs都需要花费半小时到一小时的时间,那么依赖它的应用和作业就要和倒霉,有些可以协调,但很不幸的是有些不能协调……
问题既然明白在什么地方了,就可以有的放矢了。可以分成两种策略,一种是优化程序,缩短时间,另一种则是想其他的办法,(比如热备,分布式
namenode或者分布式namespace,动态分割mds等)来解决这个问题。这里先谈第一种策略,就是优化(后续再谈第二种策略)。
两个时间长的地方,可以作为两个独立的问题去解决。先谈fsimage的加载:对fsimage的加载过程进行了profiling的采集,发现结果如下:
namenode启动时的程序逻辑和方法调用详细过程就不详细介绍了。从性能采集的数据来看,fsimage的 load过程几乎有95%以上的时间消耗在了FSImage.java的loadFSImage(File)函数的
for循环中,这里会针对HDFS
meta信息中所有的文件和目录进入一次循环,而如果fsimage中记录了几千万的文件和目录,那么消耗时间就可想而知了。而性能数据中惊奇的发现,调
用中最耗时的操作居然是String用来判断是否是绝对路径和String根据正则划分路径的操作,这里消耗的时间占总时间的70+%。所以,修改这部分
代码,让其性能提高了不少,具体数据就不详述了。
目前暂时进行到这里,所以未完待续……
写
到这里不免有一些感触,搞分布式和
hadoop已经有几年了,逐渐发现很多的问题都殊途同归,很多的时候,问题没有暴露,或者还不至于让人实在忍受不下去,或者不到非优化或解决的时候,很
少有人去关注某些不起眼的地方,却隐藏着影响整个全局的小问题,也许动动手指就能有很大提升的地方,不出问题之前永远也不会有人去关注。
还
有就是,大家的目光,大部分都集中在了分布式系统的宏观性能,Scalability等方面,作业提交到集群上去后,很少有人去关注作业的微观性能
(map和reduce数多少,reduce是否有长尾,是否分桶均匀,map阶段merge
sort的频率是否合理,中间结果数据量是否过大,是否需要采用中间结果压缩,shuffle时网络带宽是否是瓶颈,reduce在shuffle阶段 merge的buffer是否合理,是否存在io的瓶颈而需要用cpu来换iowait,等等等等),很多的时候,提交作业的人都不是分布式系统的开发 者,而是应用程序的开发者,作业提交上去以后,没达到预期的效果第一个想到的就是分布式的平台不够高效,不用也罢,干脆自己去开发一套新的分布式系统好了。这样的人和反馈比比皆是,却很少有人在意说,我的作业设置是否合理,应用程序的效率是否高效,影响作业运行效率的地方到底在哪里,是在分布式系统平台里还是在应用程序本身逻辑中,很少有人会去这样思考。
还
有就是,分布式系统(潮人们都喜欢称之为“云”,唬唬外行还行,搞这个的就没必要自己唬自己了),本质上真的就是一种服务,分布式系统的开发人员也好,运
维人员也好,归根结底到后来就都是服务人员,这一点,我的前司(就是“前公司”的意思,衍生自“我司”)其实就看的很准,可惜最终还是满怀遗憾的离开了那
里……