草庐IT

Hadoop1.x和2.X的HDFS fsimage和edits文件运行机制对比

zengzhaozheng 2023-03-28 原文
一、概述

    之前写过一篇非常详细的,利用QJM在HDFS2.0部署HA策略的文章,主要说了利用QJM进行HA部署以及其原理(http://zengzhaozheng.blog.51cto.com/8219051/1441170 )。但是,其中没有详细描述HADOOP2.x通过QJM部署HA完毕之后,ActiveNamenode和StandbyNamenode之间的元数据运行机制,实际上由于2.x的HA策略的引入,其元数据的运行机制和1.x比起来已经有了很大的不同。写这篇blog的目的主要是为了对hadoop1.x和hadoop2.x的元数据运行机制进行比较,当是自己的笔记吧。

二、fsp_w_picpath和edits文件的作用

    先来看看关于NameNode元数据相关的目录结构,也就是配置在hdfs-site.xml上的dfs.name.dir项,具体目录为$dfs.name.dir/current。看看目录(hadoop2.2.0版本):

我们发现有些以edites_开头和少量以fsp_w_picpath开头的文件。fsp_w_picpath和edites文件都是hadoop文件系统元数据的组成部分。

    其中fsp_w_picpath镜像文件包含了整个HDFS文件系统的所有目录和文件的indoe信息。对于文件来说包括了数据块描述信息、修改时间、访问时间等;对于目录来说包括修改时间、访问权限控制信息(目录所属用户,所在组等)等。

    另外,edit文件主要是在NameNode已经启动情况下对HDFS进行的各种更新操作进行记录,HDFS客户端执行所有的写操作都会被记录到edit文件中。

三、NameNode简单启动过程

    在HDFS中,任何一个文件,目录和block,在HDFS中都会被表示为一个object存储在namenode的内存中,每一个object占用150 bytes的内存空间。当NameNode启动的时候,首先会将fsp_w_picpath里面的所有内容映像到内存中,然后再一条一条地执行edits中的记录,然后等待各个Datanode向自己汇报块的信息来组装blockMap,从而离开安全模式。在这里涉及到BlockMap结构,所谓的BlockMap结构就是记录着block的元数据(加载在NameNode的内存中)和其对应的实际数据(存储在各个DataNode中)的映射关系。真正每个block对应到datanodes列表的信息在hadoop中并没有进行持久化存储,而是在所有datanode启动时,每个datanode对本地磁盘进行扫描,将本datanode上保存的block信息汇报给namenode,namenode在接收到每个datanode的块信息汇报后,将接收到的块信息,以及其所在的datanode信息等保存在内存中。HDFS就是通过这种块信息汇报的方式来完成 block -> datanodes list的对应表构建。Datanode向namenode汇报块信息的过程叫做blockReport,而namenode将block -> datanodes list的对应表信息保存在一个叫BlocksMap的数据结构中。因此,我们可以得出一个非常重要的结论,NameNode不会定期的向各个DataNode去”索取“块的信息,而是各个datanode定期向namenode汇报块的信息。当组装完NameNode组装完BlockMap的信息后基本上整个HDFS的启动就完成了,可以顺利地离开安全模式了。分析到这里,我们就可以很清楚地知道整个HDFS的启动速度是由上面决定的了,第一:执行各个edits文件,这个也是我这篇blog重点讨论的。第二:各个DataNode向NameNode汇报块信息的进度(当99.9%的block汇报完毕才会离开安全模式)。

四、Hadoop1.x中fsp_w_picpath和edits的合并机制

    当edits文件很多很大的时候,NameNode在启动的时候需要逐一每条的执行这些edits文件,这就严重地影响了整个HDFS的启动时间。这问题在hadoop1.x是通过SecondaryNamenode机制将edits文件合并到fsp_w_picpath中,其之得到解决,SecondaryNamenode在第一代的Hadoop中算是一个非热备的NameNode备份。整个SecondaryNamenode的工作流程简单地画了一下图:


简单描述一下具体流程:

步骤一:SSN在一个checkpoint时间点和NameNode进行通信,请求NameNode停止使用edits文件记录相关操作而是暂时将新的Write操作写到新的文件edit.new来。

步骤二:SSN通过HTTP GET的方式从NameNode中将fsp_w_picpath和edits文件下载回来本地目录中。

步骤三:SSN中合并edits和fsp_w_picpath。SSN将从NameNode中下载回来的fsp_w_picpath加载到内存中,然后逐条 执行edits文件中的各个操作项,使得加载到内存中的fsp_w_picpath中包含edits中的操作,这个过程就是所谓的合并了。

步骤四:在SSN中合并完fsp_w_picpath和edits文件后,需要将新的fsp_w_picpath回传到NameNode上,这个是通过HTTP POST方式进行的。

步骤五:NameNode将从SSN接收到的新的fsp_w_picpath替换掉旧的fsp_w_picpath。同时将edits.new文件转换为通常的edits文件,这样edits文件的大小就得到减少了。SSN整个合并以及和NameNode的交互过程到这里已经结束。

五、Hadoop2.x中fsp_w_picpath和edits的合并机制

(1)Hadoop2.x的HA策略简介

    由于HDFS2.0的HA策略的加入,使得在hadoop2.x中的fsp_w_picpath和edits的合并机制和hadoop1.x完全不同。在hadoop2.x中已经没有SecondaryNamenode,而是直接通过QJM方式配置若干奇数个JournalNode来实现NameNode热备HA策略。详细的Hadoop2.x的HA策略的原理和部署这里就不说了,可以看我之前的blog:http://zengzhaozheng.blog.51cto.com/8219051/1441170 。这里主要说说简单的HA机制以及其工作流程。在同一个集群当中同时运行着2个Namenode,一个处于Active状态,用于处理客户端的请求。另外一个处于standy状态,用于热备,其状态和active Namenode是维持一致的,当Active Namenode出现故障,Standy Namenode可以马上转化为Active Namenode。但是 2个Namenode中有且只有一个处于active状态来处理客户端的请求,否则将会产生脑裂情况。这样看来,那么客户端的一次写请求,其操作日志需要同时被记录再Active NameNode和standy NameNode中。那么疑问产生了,在保证不产生脑裂的情况下如何使得操作日志需要同时被记录再Active NameNode和standy NameNode呢?

    为了让Standby NameNode的状态和Active NameNode保持同步,即元数据保持一致,它们都将会和JournalNodes守护进程通信。当Active NameNode执行任何有关命名空间的修改,它至少需要将产生的edits持久化到N-((N-1)/2)个JournalNodes上才能保证命名空间修改的安全性,换句话说:如果你的HA策略中启动了N个JournalNode进程那么整个QJM最多允许(N-1)/2个进程死掉,这样才能保证editLog成功完整地被写入。比如 3个 JournalNode 时,最多允许 1 个 JournalNode挂掉,5个 JournalNode 时,最多允许 2 个 JournalNode 挂掉。而Standby NameNode负责观察edits log的变化,它能够读取从JNs中读取edits信息,并更新其内部的命名空间。一旦Active NameNode出现故障,Standby NameNode将会保证从JNs中读出了全部的Edits,然后切换成Active状态。Standby NameNode读取全部的edits可确保发生故障转移之前,是和Active NameNode拥有完全同步的命名空间状态。

(2)Hadoop2.x中fsp_w_picpath和edits的合并流程

步骤一:Active Namenode和Standby NameNode从JournalNodes的edits共享目录中同步edits到自己edits目录中。其中JournalNodes的edits共享目录的共享目录在配置HA策略的时候由下面参数配置:

<property>   <name>dfs.namenode.shared.edits.dir</name>   <value>qjournal://XX/xxcluster</value> </property> <property>   <name>dfs.journalnode.edits.dir</name>   <value>/journalNode/edits</value> </property>步骤二:Standby NameNode定期检查合并的条件是否成立,如果成立会合并fsp_w_picpath和edits文件;

    在Standby NameNode中会一直维护着一个叫CheckpointerThread的线程,这个线程调用StandbyCheckpointer类去每隔1000*Math.min(checkpointCheckPeriod, checkpointPeriod)秒检测一次是否要将fsp_w_picpath和从journalNode同步过来的edits做一次合并操作,其中checkpointCheckPeriod由hdfs-site.xml中的dfs.namenode.checkpoint.period 配置,checkpointPeriod则有hdfs-site.xml中的dfs.namenode.checkpoint.check.period 配置。

<property>   <name>dfs.namenode.checkpoint.period</name>   <value>3600</value>   <description>The number of seconds between two periodic checkpoints.   </description> </property> <property>   <name>dfs.namenode.checkpoint.check.period</name>   <value>60</value>   <description>The SecondaryNameNode and CheckpointNode will poll the NameNode   every 'dfs.namenode.checkpoint.check.period' seconds to query the number   of uncheckpointed transactions.   </description> </property>其中具体什么条件才符合合并条件,我们就看看看StandbyCheckpointer类的dowork方法,看我的注释就一目了然了:

 private void doWork() {       // Reset checkpoint time so that we don't always checkpoint       // on startup.       lastCheckpointTime = now();       while (shouldRun) {         try {         //每隔1000*Math.min(checkpointCheckPeriod, checkpointPeriod)秒检测一次是否要将fsp_w_picpath和从journalNode同步过来的edits做一次合并操作           Thread.sleep(1000 * checkpointConf.getCheckPeriod());         } catch (InterruptedException ie) {         }         if (!shouldRun) {           break;         }         try {           // We may have lost our ticket since last checkpoint, log in again, just in case           if (UserGroupInformation.isSecurityEnabled()) {             UserGroupInformation.getCurrentUser().checkTGTAndReloginFromKeytab();           }                      long now = now();           //获得最后一次往journalNode写入的TxId(这个可以在namenode日志或者50070界面可以看到)和最近一次做Checkpoint的TxId的差值           long uncheckpointed = countUncheckpointedTxns();           long secsSinceLast = (now - lastCheckpointTime)/1000;                      boolean needCheckpoint = false;           //第一种符合合并的情况:当最后一次往journalNode写入的TxId(这个可以在namenode日志或者50070界面可以看到)           //和最近一次做Checkpoint的TxId的差值大于或者等于dfs.namenode.checkpoint.txns配置的数量(默认1000000)时做一次合并           if (uncheckpointed >= checkpointConf.getTxnCount()) {             LOG.info("Triggering checkpoint because there have been " +                  uncheckpointed + " txns since the last checkpoint, which " +                 "exceeds the configured threshold " +                 checkpointConf.getTxnCount());             needCheckpoint = true;           }           //第二种符合合并的情况:当时间间隔大于或者等于dfs.namenode.checkpoint.period配置的时间是做合并           else if (secsSinceLast >= checkpointConf.getPeriod()) {             LOG.info("Triggering checkpoint because it has been " +                 secsSinceLast + " seconds since the last checkpoint, which " +                 "exceeds the configured interval " + checkpointConf.getPeriod());             needCheckpoint = true;           }                      synchronized (cancelLock) {             if (now < preventCheckpointsUntil) {               LOG.info("But skipping this checkpoint since we are about to failover!");               canceledCount++;               continue;             }             assert canceler == null;             canceler = new Canceler();           }                      if (needCheckpoint) {             doCheckpoint();             lastCheckpointTime = now;           }         } catch (SaveNamespaceCancelledException ce) {           LOG.info("Checkpoint was cancelled: " + ce.getMessage());           canceledCount++;         } catch (InterruptedException ie) {           // Probably requested shutdown.           continue;         } catch (Throwable t) {           LOG.error("Exception in doCheckpoint", t);         } finally {           synchronized (cancelLock) {             canceler = null;           }         }       }     }   }步骤三:Standby NameNode中的StandbyCheckpointer类合并完fsp_w_picpath和edits之后,将合并之后的fsp_w_picpath上传到Active NameNode相应目录中;

步骤四:Active NameNode接到最新的fsp_w_picpath文件之后,替换掉旧的fsp_w_picpath和清理掉edits文件;到这里整个合并过程已经完毕。

五、总结

    本文主要做了hadoop1.x和hadoop2.x的fsp_w_picpath和edits文件合并机制的对比,另外也对hadoop2.x的HA机制做了简单的介绍。写完这边blog下来自己对SSN以及hadoop2.x的edits文件的处理有了更深入清晰的认识;另外通过这样的对比,感觉自己收获最大的是对整个HDFS的启动过程有很深入的认识。


转载请注明出处:http://zengzhaozheng.blog.51cto.com/8219051/1561591 

有关Hadoop1.x和2.X的HDFS fsimage和edits文件运行机制对比的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  4. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  5. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  6. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  9. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  10. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

随机推荐