HDFS 数据块和副本状态转换

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容

  • 一、Introduction As we all know,网络是时时刻刻会出现故障的,HDFS集群庞大了之后出现...
    小北觅阅读 465评论 0 0
  • 本文来学习一下HDFS中的块状态和副本状态。参考资料:《Hadoop 2.X HDFS源码剖析》--徐鹏 HDFS...
    小北觅阅读 1,025评论 0 1
  • 1.背景 HDFS最初是参考谷歌GFS论文原理开发的一个开源产品,由Lucene开源项目的创始人Doug Cutt...
    架构禅话阅读 1,377评论 0 2
  • HDFS 在文件写入过程中会有哪些异常: Client 在写入过程中,NameNode 挂了 Client 在写入...
    wayyyy阅读 181评论 0 0
  • 一、简介 简介HDFS是Hadoop非常重要的核心之一,它也是由Doug Cutting基于Google公司03年...
    小帅明3号阅读 1,013评论 0 0