美文网首页编程相关
HDFS 数据块和副本状态转换

HDFS 数据块和副本状态转换

作者: wayyyy | 来源:发表于2022-12-04 00:07 被阅读0次

    HDFS 中文件的存储方式是将文件block进行切分,默认一个 block 64MB(这个值在hadoop2.x中是128M)。若文件大小超过一个 block 的容量(64M)就会被切分为多个 block,这些block会根据存储策略存储在不同的 DataNode 上。

    image.png

    一个文件至少由一个或多个 block 组成,而一个 block 仅属于一个文件。

    Block 或者 Replica 的状态

    文件在HDFS里进行读取和存储的时候大都是以block的形式存在和表现。每个文件都可能会有很多个block,每个block又会根据配置存在多个备份。

    在NN(NameNode)的视角,将其称为Block,在DN(DataNode)的视角将其称为Replica。

    Block 或者 Replica 在NN中和DN中随着操作以及各种异常场景,会有多种状态,这些状态因不同的操作或者事件而触发和转变。

    Replica
     static public enum ReplicaState {
        FINALIZED(0),
        RBW(1),
        RWR(2),
        RUR(3),
        TEMPORARY(4);
    }
    
    • RBW(Replica Being Written)状态
      这是正在写入副本的状态(无论是写入还是追加),RBW 副本始终是打开文件的最后一个块。RBW 副本的数据(不一定是全部)对客户端可见。

    • FINALIZED
      当副本处于此状态时,写入副本已完成并且副本中的数据被“冻结”(长度已确定),除非重新打开副本以进行追加。 具有相同生成戳(称为 GS 并在下面定义)的块的所有最终副本应该具有相同的数据。 作为恢复的结果,最终副本的 GS 可能会增加。

    • RWR(Replica Waiting to be Recovered)状态
      当一个DataNode在流水线传输过程中宕机,那么当他重启后,所有的之前在流水线中正在被写的RBW态Replica都会转换成RWR态。

    • RUR(Replica Under Recovery)
      租约过期后发生租约恢复和数据块恢复时所处的状态。

    • TEMPORARY
      这个状态是用来暂存Replica的,某个Block的最小备份数是3,原本有3台DataNode存有Replica,但是其中一台宕机了,那么NameNode需要把备份复制到新的一台DataNode A上,于是在A那创建了一个TEMP状态的Replica来将数据复制进去,如果复制成功那么又恢复到3个Replica了。如果A宕机,那么下次重启的时候,这个TEMP的Replica将被删除。

    image.png

    NameNode 不会持久化存储这些状态,一旦 NameNode 发生重启,它将所有打开文件的最后一个 block 设置为 UNDER_CONSTRUCTION 状态,其他则全部设置为 COMPLETE 状态。

    Replica 在 Datanode 中的状态变化

    datanode 会将不同状态的副本存储到磁盘的不同目录下,换句话说,datanode上Replica的状态是会被持久化的。在 datanode 的磁盘上,有3个子目录:

    1. rbw
      当Replica被客户端请求首次创建的时候,会被放入rbw目录
    2. current
      当一个Replica被finalized,就被移动到 current 目录
    3. tmp
      当Replica是因为复制或者均衡操作被创建的时候,会被放入tmp目录

    当数据节点重启时,tmp目录下的 replica 就被清空,rbw目录下的Replica就转变为 RWR 状态。

    • 从Init出发,一个新创建的 replica 有两种情况:
      由 Client 请求,新建的 replica 用于写入,状态为 RBW。
      由 NameNode 请求,新建的 replica 用于复制或集群间再平衡拷贝,状态为 TEMPORARY。

    • 从RBW出发,有三种情况:

      • Client 写完并关闭文件后,切换到 FINALIZED 状态。
      • replica 所在的 DataNode 发生重启,切换到 RWR 状态,重启期间数据可能过时了,可以被丢弃。
      • replica 参与 block recovery 过程,切换到 RUR 状态。
    • 从TEMPORARY出发,有两种情况:

      • 复制或集群间再平衡拷贝成功后,切换到 FINALIZED 状态。
      • 复制或集群间再平衡拷贝失败或者所在 DataNode 发生重启,该状态下的 replica 将被删除,注意这个状态状态上图未有体现。
    • 从RWR出发,有两种情况:

      • 所在 DataNode 挂了,重启后又回到 RWR 状态,自己到自己。
      • replica 参与 block recovery 过程,切换到 RUR 状态。
    • 从RUR出发,有两种情况:

      • DataNode 挂了,就变回了 RBW 状态,重启后只会回到 RWR 状态,看是否还有必要参与恢复还是过时直接被丢弃。
      • 恢复完成,切换到 FINALIZED 状态。
    • 从FINALIZED出发,有两种情况:

      • 文件重新被打开追加写入,文件的最后一个 block 对应的所有 replicas 切换到 RBW。
      • replica 参与 block recovery 过程,切换到 RUR 状态。
    Block
      static public enum BlockUCState {
        COMPLETE,
        UNDER_CONSTRUCTION,
        UNDER_RECOVERY,
        COMMITTED;
    }
    

    NN 视角 Block 有4中状态:

    • UnderConstruction状态
      这是一个块正在被写入(包括追加写)时的状态,处于该状态的长度和时间戳gs都是可变的,并且它的数据(不一定是全部)对读者可见。

    • COMMITTED 状态
      客户端在写文件时,每次请求新的数据块或者关闭文件时,都会顺带对上一个数据块进行提交操作。COMMITED 状态的数据块表明客户端已经把该数据块的所有数据都发送到了 Datanode 组成的数据流管道中。并且已经收到了下游的ACK响应,但是 Namenode 还没有收到任何一个 Datanode 汇报有 FINALIZED 副本。

    • COMPLETE
      数据块的大小和时间戳gs均不会再发生变化,而且NameNode已经至少收到一个DataNode节点汇报的FINALIZED状态的副本()。同时,该状态下的block会在NameNode内存中保存finalized状态副本replica的位置locations,而当文件的所有block都是COMPLETE状态的,文件才可以被关闭。

    • UNDER_RECOVERY
    image.png
    Block 在Namenode 中的状态变化
    • 从 Init 出发
      只有当 Client 新建或追加文件写入时新创建的 block 处于 UNDER_CONSTRUCTION 状态。

    • 从UNDER_CONSTRUCTION出发,有三种情况:

      • 当客户端发起 add block 或 close 请求,若处于 FINALIZED 状态的 replica 数量少于最小副本数要求,则切换到 COMMITTED 状态,这里 add block(Client每写一个block就会发起一个add block的操作,比如:一个文件有2个block,那么就会发起2次add block操作,所以,后面降到影响的是倒数第二个block) 操作影响的是文件的倒数第二个 block 的状态,而 close 影响文件最后一个 block 的状态。
      • 当客户端发起 add block 或 close 请求,若处于 FINALIZED 状态的 replica 数量达到最小副本数要求,则切换到 COMPLETE 状态
      • 若发生 block recovery,状态切换到 UNDER_RECOVERY。
    • 从UNDER_RECOVERY,有三种情况:
      • 0 字节长度的 replica 将直接被删除。
      • 恢复成功,切换到 COMPLETE。
      • NameNode 发生重启,所有打开文件的最后一个 block 会恢复成 UNDER_CONSTRUCTION 状态。
    • 从COMMITTED出发,有两种情况:
      若处于 FINALIZED 状态的 replica 数量达到最小副本数要求或者文件被强制关闭或者 NameNode 重启且不是最后一个 block,则直接切换为 COMPLETE 状态。
      • NameNode 发生重启,所有打开文件的最后一个 block 会恢复成 UNDER_CONSTRUCTION 状态。
    • 从 COMPLETE 出发
      只有在 NameNode 发生重启,所有打开文件的最后一个 block 会恢复成 UNDER_CONSTRUCTION 状态。 这种情况,若 Client 依然存活,有 Client 来关闭文件,否则由 lease recovery 过程来恢复。

    Block,BlockInfo,BlocksMap 相关类

    Block

    Block 类用来唯一标识Namenode 中的数据块,是HDFS 数据块最基本的抽象接口。
    Block 类定义了三个字段:

    blockId    // 唯一标识
    numBytes    // numBytes 是这个数据块的大小
    generationStamp    // 数据块的时间戳 
    

    其他包括序列化,反序列化的方法。

    BlockInfo
    image.png

    BlockInfo 类扩展至 Block类,是 Block类的补充和完善。

    private BlockCollection bc;
    private Object[] triplets;
    

    bc 是 类型,记录了该HDFS文件的INode 对象的引用。
    triplets保存了这个Block 副本存储在哪些数据节点上,triplets[] 这个数组的长度是3*replication,replication表示数据块的备份数。
    现在我们假设replication=3,这个数组存储的数据如下:

    image.png

    也就是说,triplets包含的信息:

    • triplets[i]:Block所在的DataNode;
    • triplets[i+1]:该DataNode上前一个Block;
    • triplets[i+2]:该DataNode上后一个Block;
      其中i表示的是Block的第i个副本,i取值[0,replication)。

    BlockInfo 中定义的方法大都是维护 这个数据结构。

    BlockInfoUnderConstruction类

    HDFS在加载fsimage时,如果当前加载的文件处于正在构建状态,则将该INodeFile的最后一个数据块设置为 BlockInfoUnderConstruction,表面最后一个数据块正在构建中,而其他的数据块均为正常的 BlockInfo 。

    BlocksMap

    HDFS为了解决通过blockId快速定位BlockInfo的问题,所以引入了BlocksMap,BlocksMap底层通过GSet(本质是一个链式解决冲突的哈希表)实现。

    在HDFS集群启动过程,DataNode会进行BR(BlockReport,其实就是将DataNode自身存储的数据块上报给NameNode),根据BR的每一个Block计算其HashCode,之后将对应的BlockInfo插入到相应位置逐渐构建起来巨大的BlocksMap。

    BlocksMap 实现比较简单,主要是维护了 Block -> BlockInfo 的映射关系。

    private final int capacity
    private final GSet<Block, BlockInfo> blocks
    

    Generation Stamp

    GS ( Generation Stamp ) :

    GS 这是 NameNode 维护的一个类似版本标签的全局唯一标识,他是一个8字节的整数,当一个NameNode格式化文件系统的时候,这个标识被初始化为1。

    以下的事件能够让GS+1:

    1. 当客户端请求NameNode创建一个新的文件。
    2. 当客户端请求NameNode打开一个文件来以便向里面追加(append)内容或者删减内容(truncate)。
    3. 当客户端在流水线工作过程中失败,需要恢复流水线,客户端回向NameNode讨要一个新的GS。
    4. NameNode以客户端的名义续租(Lease Recovery) ,GS + 1后将被写入到NameNode的日志记录里。

    可以简单理解,GS 是HDFS维护的文件系统的版本标签。当某些退出DataNode集群很久的节点加入时,根据GS可以识别出他们是否是旧的节点。

    BGS

    BGS用来标记一个Block(以及他的Replica)的版本,用来以区分Replica是否过期。

    当客户端需要写入或者追加时,都需要调用 addBlock 获取一个新的Block,NameNode会为这个Block打上一个新的BGS。新BGS产生方式很简单,NameNode将现在的GS + 1就得到了新的BGS(NameNode同时要把 + 1后的GS写到日志里)。



    参考资料
    1、https://www.codercto.com/a/47336.html
    2、https://lausaa.github.io/2021/06/08/PipelineOfHDFS/
    3、https://www.cnblogs.com/lqlqlq/p/12314057.html

    相关文章

      网友评论

        本文标题:HDFS 数据块和副本状态转换

        本文链接:https://www.haomeiwen.com/subject/rhznxdtx.html