Hadoop namenode启动瓶颈分析

转载:http://blog.csdn.net/AE86_FC/archive/2010/08/26/5842020.aspx

NameNode启动过程详细剖析

NameNode中几个关键的数据结构

FSImage

Namenode会将HDFS的文件和目录元数据存储在一个叫fsimage的二进制文件中,每次保存fsimage之后到下次保存之间的所有hdfs操作,将会记录在editlog文件中,当editlog达到一定的大小(bytes,由fs.checkpoint.size参数定义)或从上次保存过后一定时间段过后(sec,由fs.checkpoint.period参数定义),namenode会重新将内存中对整个HDFS的目录树和文件元数据刷到fsimage文件中。Namenode就是通过这种方式来保证HDFS中元数据信息的安全性。

Fsimage是一个二进制文件,当中记录了HDFS中所有文件和目录的元数据信息,在我的hadoop的HDFS版中,该文件的中保存文件和目录的格式如下:

 2011042616391875.jpg

当namenode重启加载fsimage时,就是按照如下格式协议从文件流中加载元数据信息。从fsimag的存储格式可以看出,fsimage保存有如下信息:

1.         首先是一个image head,其中包含:

a)         imgVersion(int):当前image的版本信息

b)        namespaceID(int):用来确保别的HDFS instance中的datanode不会误连上当前NN。

c)         numFiles(long):整个文件系统中包含有多少文件和目录

d)        genStamp(long):生成该image时的时间戳信息。

2.         接下来便是对每个文件或目录的源数据信息,如果是目录,则包含以下信息:

a)         path(String):该目录的路径,如”/user/build/build-index”

b)        replications(short):副本数(目录虽然没有副本,但这里记录的目录副本数也为3)

c)         mtime(long):该目录的修改时间的时间戳信息

d)        atime(long):该目录的访问时间的时间戳信息

e)         blocksize(long):目录的blocksize都为0

f)         numBlocks(int):实际有多少个文件块,目录的该值都为-1,表示该item为目录

g)        nsQuota(long):namespace Quota值,若没加Quota限制则为-1

h)        dsQuota(long):disk Quota值,若没加限制则也为-1

i)          username(String):该目录的所属用户名

j)          group(String):该目录的所属组

k)        permission(short):该目录的permission信息,如644等,有一个short来记录。

3.         若从fsimage中读到的item是一个文件,则还会额外包含如下信息:

a)         blockid(long):属于该文件的block的blockid,

b)        numBytes(long):该block的大小

c)         genStamp(long):该block的时间戳

当该文件对应的numBlocks数不为1,而是大于1时,表示该文件对应有多个block信息,此时紧接在该fsimage之后的就会有多个blockid,numBytes和genStamp信息。

因此,在namenode启动时,就需要对fsimage按照如下格式进行顺序的加载,以将fsimage中记录的HDFS元数据信息加载到内存中。

BlockMap

从以上fsimage中加载如namenode内存中的信息中可以很明显的看出,在fsimage中,并没有记录每一个block对应到哪几个datanodes的对应表信息,而只是存储了所有的关于namespace的相关信息。而真正每个block对应到datanodes列表的信息在hadoop中并没有进行持久化存储,而是在所有datanode启动时,每个datanode对本地磁盘进行扫描,将本datanode上保存的block信息汇报给namenode,namenode在接收到每个datanode的块信息汇报后,将接收到的块信息,以及其所在的datanode信息等保存在内存中。HDFS就是通过这种块信息汇报的方式来完成 block -> datanodes list的对应表构建。Datanode向namenode汇报块信息的过程叫做blockReport,而namenode将block -> datanodes list的对应表信息保存在一个叫BlocksMap的数据结构中。

BlocksMap的内部数据结构如下:   

 2011042616393515.jpg

如上图显示,BlocksMap实际上就是一个Block对象对BlockInfo对象的一个Map表,其中Block对象中只记录了blockid,block大小以及时间戳信息,这些信息在fsimage中都有记录。而BlockInfo是从Block对象继承而来,因此除了Block对象中保存的信息外,还包括代表该block所属的HDFS文件的INodeFile对象引用以及该block所属datanodes列表的信息(即上图中的DN1,DN2,DN3,该数据结构会在下文详述)。

因此在namenode启动并加载fsimage完成之后,实际上BlocksMap中的key,也就是Block对象都已经加载到BlocksMap中,每个key对应的value(BlockInfo)中,除了表示其所属的datanodes列表的数组为空外,其他信息也都已经成功加载。所以可以说:fsimage加载完毕后,BlocksMap中仅缺少每个块对应到其所属的datanodes list的对应关系信息。所缺这些信息,就是通过上文提到的从各datanode接收blockReport来构建。当所有的datanode汇报给namenode的blockReport处理完毕后,BlocksMap整个结构也就构建完成。

BlockMap中datanode列表数据结构

在BlockInfo中,将该block所属的datanodes列表保存在一个Object[]数组中,但该数组不仅仅保存了datanodes列表,还包含了额外的信息。实际上该数组保存了如下信息:

 2011042616395322.jpg

上图表示一个block包含有三个副本,分别放置在DN1,DN2和DN3三个datanode上,每个datanode对应一个三元组,该三元组中的第二个元素,即上图中prev block所指的是该block在该datanode上的前一个BlockInfo引用。第三个元素,也就是上图中next Block所指的是该block在该datanode上的下一个BlockInfo引用。每个block有多少个副本,其对应的BlockInfo对象中就会有多少个这种三元组。

       Namenode采用这种结构来保存block->datanode list的目的在于节约namenode内存。由于namenode将block->datanodes的对应关系保存在了内存当中,随着HDFS中文件数的增加,block数也会相应的增加,namenode为了保存block->datanodes的信息已经耗费了相当多的内存,如果还像这种方式一样的保存datanode->block list的对应表,势必耗费更多的内存,而且在实际应用中,要查一个datanode上保存的block list的应用实际上非常的少,大部分情况下是要根据block来查datanode列表,所以namenode中通过上图的方式来保存block->datanode list的对应关系,当需要查询datanode->block list的对应关系时,只需要沿着该数据结构中next Block的指向关系,就能得出结果,而又无需保存datanode->block list在内存中。

NameNode启动过程

fsimage加载过程

Fsimage加载过程完成的操作主要是为了:

1.         从fsimage中读取该HDFS中保存的每一个目录和每一个文件

2.         初始化每个目录和文件的元数据信息

3.         根据目录和文件的路径,构造出整个namespace在内存中的镜像

4.         如果是文件,则读取出该文件包含的所有blockid,并插入到BlocksMap中。

整个加载流程如下图所示:

 2011042616401058.png

如上图所示,namenode在加载fsimage过程其实非常简单,就是从fsimage中不停的顺序读取文件和目录的元数据信息,并在内存中构建整个namespace,同时将每个文件对应的blockid保存入BlocksMap中,此时BlocksMap中每个block对应的datanodes列表暂时为空。当fsimage加载完毕后,整个HDFS的目录结构在内存中就已经初始化完毕,所缺的就是每个文件对应的block对应的datanode列表信息。这些信息需要从datanode的blockReport中获取,所以加载fsimage完毕后,namenode进程进入rpc等待状态,等待所有的datanodes发送blockReports。

blockReport阶段

每个datanode在启动时都会扫描其机器上对应保存hdfs block的目录下(dfs.data.dir)所保存的所有文件块,然后通过namenode的rpc调用将这些block信息以一个long数组的方式发送给namenode,namenode在接收到一个datanode的blockReport rpc调用后,从rpc中解析出block数组,并将这些接收到的blocks插入到BlocksMap表中,由于此时BlocksMap缺少的仅仅是每个block对应的datanode信息,而namenoe能从report中获知当前report上来的是哪个datanode的块信息,所以,blockReport过程实际上就是namenode在接收到块信息汇报后,填充BlocksMap中每个block对应的datanodes列表的三元组信息的过程。其流程如下图所示:

 2011042616402688.png

当所有的datanode汇报完block,namenode针对每个datanode的汇报进行过处理后,namenode的启动过程到此结束。此时BlocksMap中block->datanodes的对应关系已经初始化完毕。如果此时已经达到安全模式的推出阈值,则hdfs主动退出安全模式,开始提供服务。

启动过程数据采集和瓶颈分析

对namenode的整个启动过程有了详细了解之后,就可以对其启动过程中各阶段各函数的调用耗时进行profiling的采集,数据的profiling仍然分为两个阶段,即fsimage加载阶段和blockReport阶段。

fsimage加载阶段性能数据采集和瓶颈分析

以下是对建库集群真实的fsimage加载过程的的性能采集数据:

 2011042616404481.jpg

从上图可以看出,fsimage的加载过程那个中,主要耗时的操作分别分布在FSDirectory.addToParentFSImage.readString,以及PermissionStatus.read三个操作,这三个操作分别占用了加载过程的73%,15%以及8%,加起来总共消耗了整个加载过程的96%。而其中FSImage.readStringPermissionStatus.read操作都是从fsimage的文件流中读取数据(分别是读取String和short)的操作,这种操作优化的空间不大,但是通过调整该文件流的Buffer大小来提高少许性能。而FSDirectory.addToParent的调用却占用了整个加载过程的73%,所以该调用中的优化空间比较大。

       以下是addToParent调用中的profiling数据:

 2011042616405845.jpg

从以上数据可以看出addToParent调用占用的73%的耗时中,有66%都耗在了INode.getPathComponents调用上,而这66%分别有36%消耗在INode.getPathNames调用,30%消耗在INode.getPathComponents调用。这两个耗时操作的具体分布如以下数据所示:

 2011042616411079.jpg

可以看出,消耗了36%的处理时间的INode.getPathNames操作,全部都是在通过String.split函数调用来对文件或目录路径进行切分。另外消耗了30%左右的处理时间在INode.getPathComponents中,该函数中最终耗时都耗在获取字符串的byte数组的java原生操作中。

blockReport阶段性能数据采集和瓶颈分析

由于blockReport的调用是通过datanode调用namenode的rpc调用,所以在namenode进入到等待blockreport阶段后,会分别开启rpc调用的监听线程和rpc调用的处理线程。其中rpc处理和rpc鉴定的调用耗时分布如下图所示:

 2011042616412490.jpg

而其中rpc的监听线程的优化是另外一个话题,在其他的issue中再详细讨论,且由于blockReport的操作实际上是触发的rpc处理线程,所以这里只关心rpc处理线程的性能数据。

       在namenode处理blockReport过程中的调用耗时性能数据如下:

 2011042616413668.jpg

可以看出,在namenode启动阶段,处理从各个datanode汇报上来的blockReport耗费了整个rpc处理过程中的绝大部分时间(48/49),blockReport处理逻辑中的耗时分布如下图:

 2011042616421311.jpg

从上图数据中可以发现,blockReport阶段中耗时分布主要耗时在FSNamesystem.addStoredBlock调用以及DatanodeDescriptor.reportDiff过程中,分别耗时37/48和10/48,其中FSNamesystem.addStoredBlock所进行的操作时对每一个汇报上来的block,将其于汇报上来的datanode的对应关系初始化到namenode内存中的BlocksMap表中。所以对于每一个block就会调用一次该方法。所以可以看到该方法在整个过程中调用了774819次,而另一个耗时的操作,即DatanodeDescriptor.reportDiff,该操作的过程在上文中有详细介绍,主要是为了将该datanode汇报上来的blocks跟namenode内存中的BlocksMap中进行对比,以决定那个哪些是需要添加到BlocksMap中的block,哪些是需要添加到toRemove队列中的block,以及哪些是添加到toValidate队列中的block。由于这个操作需要针对每一个汇报上来的block去查询BlocksMap,以及namenode中的其他几个map,所以该过程也非常的耗时。而且从调用次数上可以看出,reportDiff调用在启动过程中仅调用了14次(有14个datanode进行块汇报),却耗费了10/48的时间。所以reportDiff也是整个blockReport过程中非常耗时的瓶颈所在。

       同时可以看到,出了reportDiff,addStoredBlock的调用耗费了37%的时间,也就是耗费了整个blockReport时间的37/48,该方法的调用目的是为了将从datanode汇报上来的每一个block插入到BlocksMap中的操作。从该方法调用的运行数据如下图所示:

 2011042616424146.jpg

从上图可以看出,addStoredBlock中,主要耗时的两个阶段分别是FSNamesystem.countNode和DatanodeDescriptor.addBlock,后者是java中的插表操作,而FSNamesystem.countNode调用的目的是为了统计在BlocksMap中,每一个block对应的各副本中,有几个是live状态,几个是decommission状态,几个是Corrupt状态。而在namenode的启动初始化阶段,用来保存corrput状态和decommission状态的block的map都还是空状态,并且程序逻辑中要得到的仅仅是出于live状态的block数,所以,这里的countNoes调用在namenode启动初始化阶段并无需统计每个block对应的副本中的corrrput数和decommission数,而仅仅需要统计live状态的block副本数即可,这样countNodes能够在namenode启动阶段变得更轻量,以节省启动时间。

2.3 瓶颈分析总结

从profiling数据和瓶颈分歧情况来看,fsimage加载阶段的瓶颈除了在分切路径的过程中不够优以外,其他耗时的地方几乎都是在java原生接口的调用中,如从字节流读数据,以及从String对象中获取byte[]数组的操作。

       而blockReport阶段的耗时其实很大的原因是跟当前的namenode设计以及内存结构有关,比较明显的不优之处就是在namenode启动阶段的countNode和reportDiff的必要性,这两处在namenode初始化时的blockReport阶段有一些不必要的操作浪费了时间。可以针对namenode启动阶段将必要的操作抽取出来,定制成namenode启动阶段才调用的方式,以优化namenode启动性能。

转载于:https://www.cnblogs.com/qianxun/archive/2011/04/26/2029494.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/362254.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 9 –终极功能列表

这篇文章将针对即将到来的Java 9版本进行更新,新增功能 ( 最新更新:2014年 9月9日 ) OpenJDK开发正在加快速度:2014年3月Java 8发布之后,我们预计将进入2年的发布周期。 据报道,Java 9将于2016…

js中的作用域和作用域链

作用域就是变量与函数的可访问范围。在js中只有 全局作用域 和 函数作用域 ,并没有块级作用域。 全局作用域 在所有函数外定义的变量、声明的函数就是全局作用域,在全部环境下都可以访问。 var a 111;function fn(){console.log(a); }fn(); // 打印了…

vue打包后不使用服务器直接访问方法

根据官网打包执行npm run build 后dist文件夹打开的index.html 是空白 需要开启http服务器才能访问,以下是解决办法 1、找到config文件夹下的index文件 修改成 2、找到build文件夹下的until文件 修改成 然后执行npm run build重新打包下就ok了 更多专业前端知…

OpenStack虚机网卡的创建过程

原文:https://www.sdnlab.com/20286.htmlOpenStack最基本和常用的操作就是启动虚机。虚机启动的过程中涉及很多内容,其中非常重要的一个环节就是创建并绑定虚机的虚拟网卡。虚机的创建和管理是Nova的任务,虚机网络的创建和管理是Neutron的任务…

js中的原型与原型链

js的学习有三座大山, 原型/原型链 、 作用域/闭包 、 异步/单线程,这三个知识点虽然基础但是入门时理解起来比较困难,本文先整理总结原型和原型链这一知识点。 1. 原型链怎么来的?对象的原型和function的prototype属性有什么关系…

HTML5 audio 如何实现播放多个MP3音频

<audio>标签是HTML5中的新标签&#xff0c;定义声音用于嵌入音频内容&#xff0c;比如音乐或其他音频流。用的比较多音频格式是.mp3。 <audio>标签常用属性如下表 属性值描述autoplayautoplay添加该属性后&#xff0c;音频会自动播放controlscontrols设置后&…

windwos下ntp服务器配置 arm平台ntp客户端获取同步时间

项目需要使用同步时间&#xff0c;在arm-linux开发板上&#xff0c;移植了ntp客户端&#xff0c;查看了一些资料&#xff0c;最终发现使用windows自带的ntp服务器比较方便&#xff0c;而且很靠谱&#xff0c;使用配置了一番&#xff0c;已经能够正常使用 详细步骤&#xff1a; …

BOM(Browser Object Model)

BOM&#xff08;浏览器对象模型&#xff09;&#xff0c;提供了一系列操作浏览器&#xff0c;获取浏览器信息的接口。这些接口在平时的工作中会经常用到&#xff0c;例如当前页面的刷新&#xff0c;获取url的参数等等。 注&#xff1a;图片来自 http://www.dreamdu.com/javascr…

入门 IT 行业,该具备哪些技能?

对于刚开始进入IT的新人来说&#xff0c;“必备技能”往往意味着一个长长的、标有重要度的学习列表&#xff0c;但是过长的列表通常会导致新人不知如何开始学习&#xff0c;压力倍增。本文尝试列举出最重要的几个技能&#xff0c;也期望通过此列表能给新人一个比较明确的学习重…

实验七作业

Part 1:验证性实验 将line29&#xff1a;for(i0;i<N;i)改为while(!feof(fp)) // 从文本文件file1.dat中读取数据&#xff0c;找出最高分和最低分学生信息&#xff0c;并输出在屏幕上 #include <stdio.h> #include <stdlib.h>#define N 10// 定义一个结构体类型…

块级格式化上下文(Block Formatting Context)

CSS块级格式化上下文是块级盒子的一种能力&#xff0c;这种能力并不是直接通过css属性声明而获得的&#xff0c;而是添加css的一部分相关属性之后自动获得的能力&#xff0c;也就是说没有一个明确的属性就是生成块级格式化上下文的。 块级格式化上下文的能力就是让具有该能力的…

前端性能优化方法总结

一个网站前端性能的好坏很大程度上影响了用户愿不愿意使用访问这个网站&#xff0c;因此对前端进行性能优化是个很重要的事情。  对于前端性能优化这个问题&#xff0c;主要学习自yahoo前端性能团队总结的35条黄金定律总结&#xff0c;觉得很全很赞&#xff0c;做个学习总结和…

Akka笔记–演员介绍

过去做过多线程的任何人都不会否认管理多线程应用程序有多么艰辛和痛苦。 我说管理是因为它一开始很简单&#xff0c;一旦您开始看到性能改进&#xff0c;它就会变得非常有趣。 但是&#xff0c;当您发现没有一种简单的方法可以从子任务中的错误或难以发现的僵尸错误中恢复时&a…

Java英雄:丹·艾伦

“ Java英雄 ”系列休息了很长时间。 老实说&#xff0c;我想即使有很多人想在这里收录&#xff0c;它也可能会以虚无收场。 其中之一是丹。 我第一次要求他捐款已经将近一年半了&#xff0c;与此同时发生的一切&#xff0c;让我不再有任何答案就让我安心了。 但是以下内容在Ja…

Java-Class-I:java.util.List

ylbtech-Java-Class-I&#xff1a;java.util.List1.返回顶部 1.1、import java.util.ArrayList;import java.util.List; 1.2、List<Integer> newList new ArrayList<Integer>();newList.add(3); 2、 2.返回顶部1.1、import java.util.*;public class Test{public …

推荐:个人时间跟踪工具 ManicTime

在《个人管理 &#xff0d; 目标管理之前&#xff0c;你会时间管理吗》中我介绍的时间管理三阶段之一“对时间的实际去处进行记录”时说过现在有很多时间管理工具&#xff0c;也有人希望我介绍一下我使用的工具&#xff0c;那么我就利用中午休息时间&#xff0c;马上给大家介绍…

SQL Server 2005怎样进行性能排错

很少会有偶然的性能下降。设计不良的数据库或工作负载配置不正确的系统会经常导致性能问题。管理员需要能预先阻止或最小化问题的影响&#xff0c;当管理员遇到问题时&#xff0c;应该诊断问题并采取正确操作来修复问题。本文提供了按部就班的指导&#xff0c;通过使用可用的工…

JVM PermGen –您在哪里?

这篇文章介绍了JVM内存结构的一些基础知识&#xff0c;并快速窥视了PermGen&#xff0c;以了解自Java SE 8出现以来它已消失的地方。 裸基础 JVM只是系统上运行的另一个进程&#xff0c;魔术始于java命令。 像任何OS进程一样&#xff0c;它需要内存才能运行。 记住– JVM本身是…

python6-函数

转载于:https://www.cnblogs.com/WIU1905/p/11101249.html

Windows Phone 7.1 “芒果” SDK Beta 下载地址

Windows Phone 7.1 “芒果” SDK Beta 今天早上发布&#xff0c;第一时间下载体验。功能果然激动人心。 下载地址&#xff1a; 离线ISO请点我&#xff0c; 在线安装请点我。转载于:https://www.cnblogs.com/finehappy/archive/2011/05/25/2056849.html