前言
在HDFS内,我们通常听到的最频繁的2个名词术语:副本(Replica)和块(Block).几乎可以这么说,HDFS所有涉及到文件的操作都与这两个词相关。但是大家可能对这2个概念的理解还仅仅停留在一个比较浅的层面:比如说就是一个单一的replica或是一个单一的block块。尤其是在对块的层面,一个block块在最终完成后,它会经历哪些状态过程呢?这些细节的内容就是本文所准备阐述的。
副本/块的状态
在了解副本、块在文件的写入过程中的状态变化情况之前,我们需要对它们所有可能存在的状态做一个全面的了解。
副本状态
副本的所有潜在状态可以在DataNode的数据存放目录中进行查找。在BP打头的目录下,继续往里寻找,你应该会发现一些诸如rbw、finalized、tmp这些名称的目录。不要以为这些目录名称看起来没什么特别的,其实这与副本当前所处状态是息息相关的。
下面是一个副本所有可能处于的状态:
- 1.finalized.finalized的意思是确定好了的状态,表明此副本是已经写好的完整的副本块,这种状态的副本是HDFS内最多的,放置于finalized目录下。
- 2.rbw.rbw的全称是Replica being written to,表示正在被写入的副本块。所以下次如果在DataNode的数据存储目录中看到rbw下面的文件时,就明白这些块是正在被写入的副本块了。
- 3.rwr.rwr的全称是Replica waiting to be recovered,表示的意思是等待被恢复的块。这种情况发生于DataNode突然挂掉并重启时,之前正在写的块即rbw下的块会变为rwr状态的块。因为在DataNode恢复服务之后,会涉及到租约失效,恢复操作等等。
- 4.rur.rur的名称为Replica under recovery,表示正在恢复中的副本。这个状态是紧跟着rwr状态的。
- 5.temporary.temporary表示的是一种临时状态。当一个副本因为没有达到规定副本系数,而发生副本块的复制或集群进行数据平衡时,这些过程中产生的副本块就处于temporary状态,位于tmp目录下。当这些副本块完全写入完毕,则会从tmp目录转到finalized目录下。当DataNode在这个时候重启的时候,tmp目录下的副本块会被删除。
块的状态
与副本类似,一个block同样有多种状态,主要有以下几种:
- 1.UnderConstruction。UnderConstruction状态指的是一个block块处于正在被写入的状态。
- 2.UnderRecovery。从字面意思上我们也可以理解出,这是正在被恢复的块。
- 3.Committed。Committed状态的块指的是一个块已经确定好它的字节大小与generation stamp值(可理解为版本号),但是这2个值与当前块的副本块并不匹配。
- 4.Complete。当有一个块副本的字节大小值、GS值与当前块匹配,则当前块被认为是Complete状态。如果一个文件的所有块都处于Complete状态,则此文件被认为写入操作结束,可以进行关闭操作了。
HDFS数据写入的Pipeline过程
这一小节,我们又将提到HDFS数据写入时非常经典的Pipeline机制了。但是在这里,本人将会介绍一些更加细节的处理。
下面是Pipeline数据写入的过程图。
图 1-1 Pipeline纵向展示图
可能很多人在这里会有一个疑惑:一个文件数据到底是以什么形式传给下一个DataNode的,一个文件先全部在一个DataNode写完,然后再下一个?下面是对此的一个解释:
Pipeline数据传输的时候是以一个个packet为单位的,一个block可以被拆分为一个或多个packet做传输。所以Pipeline中的文件写入应该是这样的:首先一个文件被分为多个block块,然后从第一个块开始,把此块再拆分成多个packet,进行一个个packet的传输。
对应上图的显示,就是一个block块被分为了5个packet的传输,每个packet传送完毕,等待后面的节点返回ack回复,当Pipeline中所有的节点都返回ack确认信息后,表明此packet已被所有节点成功接收,可以进行下一个packet的传输。
上图中所示的过程可以分为3个子过程:
第一个过程,Pipeline的建立,时间从t0到t1时刻。
第二个过程,数据流的传输,时间从t1到t2时刻。
第三个过程,Pipeline的关闭,时间从t2到t3时刻。
上图显示的过程是一个纵向的过程,下面我们来看一个横向的过程图,这个图是我们比较熟悉的图。
图 1-2 Pipeline横向展示图
从上图可以看出,在Pipeline中同时会做3种事情:
箭头1.数据流的下游传输。
箭头2.写入数据到磁盘文件。
箭头3.数据流的反馈。
这里的数据写入过程和ack回复过程分属于不同的线程,所以无法保证2的操作和3的操作是有序的。那么问题来了,HDFS对外如何表现数据的一致性呢?继续来看下面的内容。
块数据的一致性保证
在讲述块一致性内容之前,需要引入2个新的概念:
1.BA(Bytes Acknowledged)。指的是DataNode已经回复收到的字节大小,这部分数据就是对用户可见的数据。
2.BR(Bytes Received)。指DataNode已经接收到此block的字节大小,包括当前缓冲区的数据以及写入磁盘文件的数据。
下面我们结合图1-2来理解这2个概念,假如当前Pipeline已经成功写入完一个block的a字节大小数据,此时各个DataNode的(BA, BR)=(a, a)。
当经过1过程的步骤后,DataNode1的(BA, BR)将会变为(a, a+b)。
当经过3过程的步骤后,DataNode1的(BA, BR)将会变为(a+b, a+b)。
通过Pipeline写入过程的特征,我们可以得出下面的结论:
BA1<BA2<...<BAN<BRN<...<BR2<BR1
这个式子囊括了下面几个意思:
- 在同一个节点上,BR值不会小于它的BA值。
- BR值的大小顺序是,越靠近前面的节点,它的BR值越大,BA值则恰恰相反,因为ack回复的顺序是从后往前的。
所以在这里,我们保证数据对外一致性的重要依靠就是BA值,在所有副本中取出最小的BA值,即是对用户可见的数据。但是HDFS内部对此
的设计不会这么设计文档。
Pipeline的失败恢复
在了解Pipeline的失败恢复之前,来看一种特殊情况的Pipeline的建立:Append下的Pipeline建立。
Append操作是写数据操作中比较特殊的一种,它是直接在现有文件末尾进行数据的追加操作。那么这种操作它也会存在建立Pipeline的过程吗?答案是有的。
当待追加写的一个文件的最后一个块不是写满的状态时,则直接在此块上建立Pipeline,否则新建一个块进行Pipeline的建立。新建一个块的Pipeline的建立与正常情况下写空文件的过程是一致的。
在Pipeline数据读写的整个过程中,总共有3个地方会存在失败的情况:
1.Pipeline初始建立的时候。
2.Pipeline数据传输的时候。
3.Pipeline关闭的时候。
HDFS的设计者考虑的很周到,对这3种情况都做了对应的恢复过程。
第一种情况,Pipeline开始建立的连接的时候出错了。此时DataNode会检测到失败的情况,它会关闭当前创建的block文件以及tcp连接,同时发送一个失败的ack回复给上游节点。随后,DFS客户端会从剩余的节点中寻找新的节点来重建Pipeline。因为这个过程中还没有实际写入数据,所以无需进行数据恢复。
第二种情况,Pipeline在数据传输传输的过程中出错了。然后DFS客户端会立即停止向此Pipeline写入数据。然后同样从剩余的节点中选出节点进行Pipeline的重建,此时block的generation stamp值将会被增加。然后是进行数据的恢复过程,DFS客户端会从最小的BR字节进行发送,如果新的Pipeline节点中已经包含此packet的数据了,则直接跳过发给下游的DataNode。
第三种情况,Pipeline在关闭的时候出错了。同样会在剩余可用的节点内选择节点,进行Pipeline的重建,这里重建的目的是为了执行完最后block的finalize确认动作,此过程中block的generation stamp值也会被增加。
块的恢复
这里快的恢复指的是前面内容中提到的rwr(replica wait recovery)、rur(replica under recovery)状态的情况。发生的场景是DataNod突然失败重启之后,rbw状态的副本就会变为rwr状态。这里“恢复”的对象不是文件数据,而是block长度,generation stamp值。因为DataNode的突然重启会导致之前正在写的块副本出现信息不一致的情况,这里需要重新恢复到一致的状态。而Block Recovery干的就是这个事情。恢复的具体细节这里不展开描述,大致为首先选出一个主节点上的block,然后以此块为标准,进行块长度与GS值的统一、确认。
块、副本状态转化图
下面是2张来自Hadoop社区文档中的块、副本各个状态之间的转化图。
图 1-3 副本状态转化图
图 1-4 块状态转化图
本文的所讲述的内容会偏理论,可能会比较难懂,建议大家反复阅读理解,最好能阅读英文原版设计文档。
参考资料
[1].Revisit append
[2].appendDesign3.pdf