一、概述

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

二、fsp_w_picpath和edits文件的作用

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

Hadoop两个版本fsimage和edits文件运行机制对比_第1张图片

我们发现有些以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的工作 流程简单地画了一下图:

Hadoop两个版本fsimage和edits文件运行机制对比_第2张图片

 

简单描述一下具体流程:

步骤一: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策略的原理和部署这里就不说了这里主要说说简单的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策略的时候由下面参数配置:

  dfs.namenode.shared.edits.dir

  qjournal://XX/xxcluster

  dfs.journalnode.edits.dir

  /journalNode/edits

步骤二: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 配置。

  dfs.namenode.checkpoint.period

  3600

  The number of seconds between two periodic checkpoints.

 

  dfs.namenode.checkpoint.check.period

  60

  The SecondaryNameNode and CheckpointNode will poll the NameNode

  every 'dfs.namenode.checkpoint.check.period' seconds to query the number

  of uncheckpointed transactions.

 


其中具体什么条件才符合合并条件,我们就看看看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的启动过程有很深入的认识。