Hadoop两个版本fsimage和edits文件运行机制对比

一、概述

这篇blog的目的主要是为了对hadoop1.x和hadoop2.x的元数据运行机制进行比较。

二、fsimage和edits文件的作用

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

wKioL1Qnpz3g18tkAAUUEvhuqgI113.jpg

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

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

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

三、NameNode简单启动过程

    在 HDFS中,任何一个文件,目录和block,在HDFS中都会被表示为一个object存储在namenode的内存中,每一个object占用150 bytes的内存空间。当NameNode启动的时候,首先会将fsimage里面的所有内容映像到内存中,然后再一条一条地执行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中fsimage和edits的合并机制

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

wKiom1Q0tVTQqXyxAAHWWDHGvhQ155.jpg

 

简单描述一下具体流程:

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

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

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

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

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

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

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

    由于HDFS2.0的HA策略的加入,使得在hadoop2.x中的fsimage和edits的合并机制和hadoop1.x完全不同。在 hadoop2.x中已经没有SecondaryNamenode,而是直接通过QJM方式配置若干奇数个JournalNode来实现NameNode 热备HA策略。详细的Hadoop2.x的HA策略的原理和部署这里就不说了这里主要说说简单的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中fsimage和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定期检查合并的条件是否成立,如果成立会合并fsimage和edits文件;

    在Standby NameNode中会一直维护着一个叫CheckpointerThread的线程,这个线程调用StandbyCheckpointer类去每隔 1000*Math.min(checkpointCheckPeriod, checkpointPeriod)秒检测一次是否要将fsimage和从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)秒检测一次是否要将fsimage和从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类合并完fsimage和edits之后,将合并之后的fsimage上传到Active NameNode相应目录中;

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

五、总结

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


你可能感兴趣的:(hadoop,HA,FSImage,edits)