原文地址:
https://www.cnblogs.com/wxplmm/p/7239342.html
http://www.cnblogs.com/mindwind/p/4833098.html
1. 概览
首先我们来认识一下HDFS, HDFS(Hadoop Distributed File System )Hadoop分布式文件系统。它其实是将一个大文件分成若干块保存在不同服务器的多个节点中。通过联网让用户感觉像是在本地一样查看文件,为了降低文件丢失造成的错误,它会为每个小文件复制多个副本(默认为三个),以此来实现多机器上的多用户分享文件和存储空间。
HDFS特点:
① 保存多个副本,且提供容错机制,副本丢失或宕机自动恢复。默认存3份。
② 运行在廉价的机器上。
③ 适合大数据的处理。因为小文件也占用一个块,小文件越多(1000个1k文件)块越 多,NameNode压力越大。
如:将一个大文件分成三块A、B、C的存储方式
数据复制原则:
除了最后一个块之外的文件中的所有块都是相同的大小。
HDFS的放置策略:
是将一个副本放在本地机架中的一个节点上,另一个位于不同(远程)机架中的节点上,而最后一个位于不同节点上远程机架。
涉及到的属性:
块大小:Hadoop1版本里默认为64M,Hadoop2版本里默认为128M
复制因子:每个文件加上其文件副本的份数
2.HDFS基本结构
如上图所示,HDFS基本结构分NameNode、SecondaryNameNode、DataNode这几个。
**NameNode:**是Master节点,有点类似Linux里的根目录。管理数据块映射;处理客户端的读写请求;配置副本策略;管理HDFS的名称空间;
**SecondaryNameNode:**保存着NameNode的部分信息(不是全部信息NameNode宕掉之后恢复数据用),是NameNode的冷备份;合并fsimage和edits然后再发给namenode。(防止edits过大的一种解决方案)
**DataNode:**负责存储client发来的数据块block;执行数据块的读写操作。是NameNode的小弟。
热备份
:b是a的热备份,如果a坏掉。那么b马上运行代替a的工作。
冷备份
:b是a的冷备份,如果a坏掉。那么b不能马上代替a工作。但是b上存储a的一些信息,减少a坏掉之后的损失。
fsimage
:元数据镜像文件(文件系统的目录树。)
edits
:元数据的操作日志(针对文件系统做的修改操作记录)
namenode内存中存储的是=fsimage+edits。
2.1 NameNode
2.1.1 简介
Namenode起一个统领的作用,用户通过namenode来实现对其他数据的访问和操作,类似于root根目录的感觉。
Namenode包含:目录与数据块之间的关系(靠fsimage和edits来实现),数据块和节点之间的关系
fsimage文件与edits文件是Namenode结点上的核心文件。
Namenode中仅仅存储目录树信息,而关于BLOCK的位置信息则是从各个Datanode上传到Namenode上的。
Namenode的目录树信息就是物理的存储在fsimage这个文件中的,当Namenode启动的时候会首先读取fsimage这个文件,将目录树信息装载到内存中。
而edits存储的是日志信息,在Namenode启动后所有对目录结构的增加,删除,修改等操作都会记录到edits文件中,并不会同步的记录在fsimage中。
而当Namenode结点关闭的时候,也不会将fsimage与edits文件进行合并,这个合并的过程实际上是发生在Namenode启动的过程中。
也就是说,当Namenode启动的时候,首先装载fsimage文件,然后在应用edits文件,最后还会将最新的目录树信息更新到新的fsimage文件中,然后启用新的edits文件。
整个流程是没有问题的,但是有个小瑕疵,就是如果Namenode在启动后发生的改变过多,会导致edits文件变得非常大,大得程度与Namenode的更新频率有关系。
那么在下一次Namenode启动的过程中,读取了fsimage文件后,会应用这个无比大的edits文件,导致启动时间变长,并且不可控,可能需要启动几个小时也说不定。
Namenode的edits文件过大的问题,也就是SecondeNamenode要解决的主要问题。
SecondNamenode会按照一定规则被唤醒,然后进行fsimage文件与edits文件的合并,防止edits文件过大,导致Namenode启动时间过长。
2.1.1 NameNode启动过程(待完善)
启动过程中需要检查块状态么?这是DataNode检测完了告知NameNode还是NameNode主动通知?
2.2 DataNode
2.2.1 简介
DataNode在HDFS中真正存储数据。
首先解释块(block)的概念:
DataNode在存储数据的时候是按照block为单位读写数据的。block是hdfs读写数据的基本单位。
假设文件大小是100GB,从字节位置0开始,每128MB字节划分为一个block,依此类推,可以划分出很多的block。每个block就是128MB大小。
block本质上是一个 逻辑概念,意味着block里面不会真正的存储数据,只是划分文件的。
block里也会存副本,副本优点是安全,缺点是占空间
2.2.2 DataNode启动过程(待完善)
2.2.3 新DataNode加入集群的过程(待完善)
一加入就会rebanlance么?
2.3 SecondaryNode
从NameNode上 下载元数据信息(fsimage,edits),然后把二者合并,生成新的fsimage,在本地保存,并将其推送到NameNode,同时重置NameNode的edits.
2.3.1 和NameNode交互过程(待完善)
3. client读写HDFS过程
3.1 写操作
有一个文件FileA,100M大小。Client将FileA写入到HDFS上。
HDFS按默认配置。
HDFS分布在三个机架上Rack1,Rack2,Rack3。
过程:
a. Client将FileA按64M分块。分成两块,block1和Block2;
b. Client向nameNode发送写数据请求,如图蓝色虚线①——>。
c. NameNode节点,记录block信息。并返回可用的DataNode,如粉色虚线②———>。如下:
Block1: host2,host1,host3
Block2: host7,host8,host4
**原理:**
NameNode具有RackAware机架感知功能,这个可以配置。
若client为DataNode节点,那存储block时,规则为:副本1,同client的节点上;副本2,不同机架节点上;副本3,同第二个副本机架的另一个节点上;其他副本随机挑选。
若client不为DataNode节点,那存储block时,规则为:副本1,随机选择一个节点上;副本2,不同副本1,机架上;副本3,同副本2相同的另一个节点上;其他副本随机挑选。
d. client向DataNode发送block1;发送过程是以流式写入。
**流式写入过程:**
1. 将64M的block1按64k的package划分;
2. 然后将第一个package发送给host2;
3. host2接收完后,将第一个package发送给host1,同时client向host2发送第二个package;
4. host1接收完第一个package后,发送给host3,同时接收host2发来的第二个package。
5. 以此类推,如图红线实线所示,直到将block1发送完毕。
6. host2,host1,host3向NameNode,host2向Client发送通知,说“消息发送完了”。如图粉红颜色实线所示。
7. client收到host2发来的消息后,向namenode发送消息,说我写完了。这样就真完成了。如图黄色粗实线
e. 发送完block1后,再向host7,host8,host4发送block2,如图蓝色实线所示。
f. 发送完block2后,host7,host8,host4向NameNode,host7向Client发送通知,如图浅绿色实线所示。
g. client向NameNode发送消息,说我写完了,如图黄色粗实线。。。这样就完毕了。
分析,通过写过程,我们可以了解到:
①写1T文件,我们需要3T的存储,3T的网络流量贷款。
②在执行读或写的过程中,NameNode和DataNode通过HeartBeat进行保存通信,确定DataNode活着。如果发现DataNode死掉了,就将死掉的DataNode上的数据,放到其他节点去。读取时,要读其他节点去。
③挂掉一个节点,没关系,还有其他节点可以备份;甚至,挂掉某一个机架,也没关系;其他机架上,也有备份。
3.1.1 小结
- 写操作时,client负责将文件切分成HDFS块大小;
- 在client向DataNode发送数据时,client只向一个DataNode传输数据,由该DataNode负责向其他DataNode传输副本(该DataNode如何直到其他副本的列表的?)
- 每个block存储的DataNode列表不同。如果某个文件分为2个块,则存储该文件的DataNode可能大于副本数。
3.1.2 写入过程中的故障恢复
问题:在client写入数据时,怎么样才算写入成功,需要所有的DataNode都返回成功么?
这个问题是否定的,在写入时,只需要成功写入的DataNode数大于dfs.replication.min(默认为1),那么就任务是写成功的。
写异常与恢复
HDFS写入过程中可能发生多种不同的错误异常对应着不同的处理方式。先看看有哪些可能的异常?
异常模式
可能的异常模式如下所列:
Client 在写入过程中,自己挂了
Client 在写入过程中,有 DataNode 挂了
Client 在写入过程中,NameNode 挂了
对于以上所列的异常模式,都有分别对应的恢复模式。
恢复模式
(1)client 自己挂了
当 Client 在写入过程中,自己挂了。由于 Client 在写文件之前需要向 NameNode 申请该文件的租约(lease),只有持有租约才允许写入,而且租约需要定期续约。所以当 Client 挂了后租约会超时,HDFS 在超时后会释放该文件的租约并关闭该文件,避免文件一直被这个挂掉的 Client 独占导致其他人不能写入。这个过程称为
lease recovery。
在发起 lease recovery 时,若多个文件 block 副本在多个 DataNodes 上处于不一致的状态,首先需要将其恢复到一致长度的状态。这个过程称为 block recovery。 这个过程只能在 lease recovery 过程中发起。
(2)写入时,DataNode挂了
当 Client 在写入过程中,有 DataNode 挂了。写入过程不会立刻终止(如果立刻终止,易用性和可用性都太不友好),取而代之 HDFS 尝试从流水线中摘除挂了的 DataNode 并恢复写入,这个过程称为
pipeline recovery。
(3)NameNode挂了
当 Client 在写入过程中,NameNode 挂了。这里的前提是已经开始写入了,所以 NameNode 已经完成了对 DataNode 的分配,若一开始 NameNode 就挂了,整个 HDFS 是不可用的所以也无法开始写入。流水线写入过程中,当一个 block 写完后需向 NameNode 报告其状态,这时 NameNode 挂了,状态报告失败,但不影响 DataNode 的流线工作,数据先被保存下来,但最后一步 Client 写完向 NameNode 请求关闭文件时会出错,由于 NameNode 的单点特性,所以无法自动恢复,需人工介入恢复。
上面先简单介绍了对应异常的恢复模式,详细过程后文再描述。在介绍详细恢复过程前,需要了解文件数据状态的概念。因为写文件过程中异常和恢复会对数据状态产生影响,我们知道 HDFS 文件至少由 1 个或多个 block 构成,因此每个 block 都有其相应的状态,由于文件的元数据在 NameNode 中管理而文件数据本身在 DataNode 中管理,为了区分文件 block 分别在 NameNode 和 DataNode 上下文语境中的区别,下面我们会用
replica(副本)特指在 DataNode 中的 block,而 block 则限定为在 NameNode 中的文件块元数据信息
。在这个语义限定下 NameNode 中的 block 实际对应 DataNodes 上的多个 replicas,它们分别有不同的数据状态。
那replica 和 block 分别在 DataNode 和 NameNode 中都存在哪些状态?
(1)Replica状态
Replica 在 DataNode 中存在的状态列表如下:
- FINALIZED:表明 replica 的写入已经完成,长度已确定,除非该 replica 被重新打开并追加写入。
- RBW:该状态是 Replica Being Written 的缩写,表明该 replica 正在被写入,正在被写入的 replica 总是打开文件的最后一个块。
- RWR:该状态是 Replica Waiting to be Recovered 的缩写,假如写入过程中 DataNode 挂了重启后,其上处于 RBW 状态的 replica 将被变更为 RWR 状态,这个状态说明其数据需要恢复,因为在 DataNode 挂掉期间其上的数据可能过时了。
- RUR:该状态是 Replica Under Recovery 的缩写,表明该 replica 正处于恢复过程中。
- TEMPORARY:一个临时状态的 replica 是因为复制或者集群平衡的需要而创建的,若复制失败或其所在的 DataNode 发生重启,所有临时状态的 replica 会被删除。临时态的 replica 对外部 Client 来说是不可见的。
DataNode 会持久化存储 replica 的状态,每个数据目录都包含了三个子目录:
- current:目录包含了 FINALIZED 状态 replicas。
- tmp:目录包含了 TEMPORARY 状态的 replicas。
-
rbw:目录则包含了 RBW、RWR 和 RUR 三种状态的 relicas,从该目录下加载的 replicas 默认都处于 RWR 状态。
从目录看出实际上只持久化了三种状态,而在内存中则有五种状态,从下面的 replica 状态变迁图也可以看出这点。
我们从 Init 开始简单描述下 replica 的状态变迁图。
- 从 Init 出发,一个新创建的 replica 初始化为两种状态:
-
- 由 Client 请求新建的 replica 用于写入,状态为 RBW。
-
- 由 NameNode 请求新建的 replica 用于复制或集群间再平衡拷贝,状态为 TEMPORARY。
- 从 RBW 出发,有三种情况:
-
- Client 写完并关闭文件后,切换到 FINALIZED 状态。
-
- replica 所在的 DataNode 发生重启,切换到 RWR 状态,重启期间数据可能过时了,可以被丢弃。
-
- replica 参与 block recovery 过程(详见后文),切换到 RUR 状态。
- 从 TEMPORARY 出发,有两种情况:
-
- 复制或集群间再平衡拷贝成功后,切换到 FINALIZED 状态。
-
- 复制或集群间再平衡拷贝失败或者所在 DataNode 发生重启,该状态下的 replica 将被删除
- 从 RWR 出发,有两种情况:
-
- 所在 DataNode 挂了,就变回了 RBW 状态,因为持久化目录 rbw 包含了三种状态,重启后又回到 RWR 状态。
-
- replica 参与 block recovery 过程(详见后文),切换到 RUR 状态。
- 从 RUR 出发,有两种情况:
-
- 如上,所在 DataNode 挂了,就变回了 RBW 状态,重启后只会回到 RWR 状态,看是否还有必要参与恢复还是过时直接被丢弃。
-
- 恢复完成,切换到 FINALIZED 状态。
- 从 FINALIZED 出发,有两种情况:
-
- 文件重新被打开追加写入,文件的最后一个 block 对应的所有 replicas 切换到 RBW。
-
- replica 参与 block recovery 过程(详见后文),切换到 RUR 状态。
Block 状态
Block 在 NameNode 中存在的状态列表如下:
- UNDER_CONSTRUCTION:当新创建一个 block 或一个旧的 block 被重新打开追加时处于该状态,处于改状态的总是一个打开文件的最后一个 block。
-
UNDER_RECOVERY:
当文件租约超时,一个处于 UNDER_CONSTRUCTION 状态下 block 在 block recovery 过程开始后会变更为该状态
。 -
COMMITTED:
表明 block 数据已经不会发生变化,但向 NameNode 报告处于 FINALIZED 状态的 replica 数量少于最小副本数要求
。 - COMPLETE:当 NameNode 收到处于 FINALIZED 状态的 replica 数量达到最小副本数要求后,则切换到该状态。只有当文件的所有 block 处于该状态才可被关闭。
NameNode 不会持久化存储这些状态,一旦 NameNode 发生重启,它将所有打开文件的最后一个 block 设置为 UNDER_CONSTRUCTION 状态,其他则全部设置为 COMPLETE 状态。
下图展示了 block 的状态变化过程。
我们还是从 Init 开始简单描述下 block 的状态变迁图。
- 从 Init 出发,只有当 Client 新建或追加文件写入时新创建的 block 处于 UNDER_CONSTRUCTION 状态。
- 从 UNDER_CONSTRUCTION 出发,有三种情况:
-
- 当客户端发起 add block 或 close 请求,若处于 FINALIZED 状态的 replica 数量少于最小副本数要求,则切换到 COMMITTED 状态,
-
- 这里 add 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 状态。
-
若处于 FINALIZED 状态的 replica 数量达到最小副本数要求或者文件被强制关闭或者 NameNode 重启且不是最后一个 block,
-
- NameNode 发生重启,所有打开文件的最后一个 block 会恢复成 UNDER_CONSTRUCTION 状态。
-
从 COMPLETE 出发,只有在 NameNode 发生重启,其打开文件的最后一个 block 会恢复成 UNDER_CONSTRUCTION 状态。
这种情况,若 Client 依然存活,有 Client 来关闭文件,否则由 lease recovery 过程来恢复(详见下文)。
理解了 block 和 replica 的状态及其变化过程,我们就可以进一步详细分析上述简要提及的几种自动恢复模式。
3.1.2.1 Lease Recovery 和 Block Recovery
lease recovery 的目的是当 Client 在写入过程中挂了后,经过一定的超时时间后,收回租约并关闭文件。但在收回租约关闭文件前,需要确保文件 block 的多个副本数据一致(分布式环境下很多异常情况都可能导致多个数据节点副本不一致),若不一致就会引入 block recovery 过程进行恢复。
下面是整个恢复处理流程的简要算法描述:
- 获取包含文件最后一个 block 的所有 DataNodes。
- 指定其中一个 DataNode 作为主导恢复的节点。
- 主导节点向其他节点请求获得它们上面存储的 replica 信息。
- 主导节点收集了所有节点上的 replica 信息后,就可以比较计算出各节点上不同 replica 的最小长度。
- 主导节点向其他节点发起更新,将各自 replica 更新为最小长度值,保持各节点 replica 长度一致。
- 所有 DataNode 都同步后,主导节点向 NameNode 报告更新一致后的最终结果。
-
NameNode 更新文件 block 元数据信息,收回该文件租约,并关闭文件。
其中 3~6 步就属于 block recovery 的处理过程,这里有个疑问为什么在多个副本中选择最小长度作为最终更新一致的标准?想想写入流水线过程,如果 Client 挂掉导致写入中断后,对于流水线上的多个 DataNode 收到的数据在正常情况下应该是一致的。但在异常情况下,排在首位的收到的数据理论上最多,末位的最少,由于数据接收的确认是从末位按反方向传递到首位再到 Client 端。所以排在末位的 DataNode 上存储的数据都是实际已被确认的数据,而它上面的数据实际在不一致的情况也是最少的,所以算法里选择多个节点上最小的数据长度为标准来同步到一致状态。
</font color=purple>
3.1.2.1 Pipeline Recovery
如上图所示,pipeline 写入包括三个阶段:
- pipeline setup:Client 发送一个写请求沿着 pipeline 传递下去,最后一个 DataNode 收到后发回一个确认消息。Client 收到确认后,pipeline 设置准备完毕,可以往里面发送数据了。
- data streaming:Client 将一个 block 拆分为多个 packet 来发送(默认一个 block 64MB,太大所以需要拆分)。Client 持续往 pipeline 发送 packet,在收到 packet ack 之前允许发送 n 个 packet,n 就是 Client 的发送窗口大小(类似 TCP 滑动窗口)。
-
close:Client 在所有发出的 packet 都收到确认后发送一个 Close 请求,
pipeline 上的 DataNode 收到 Close 后将相应 replica 修改为 FINALIZED 状态,并向 NameNode 发送 block 报告。NameNode 将根据报告的 FINALIZED 状态的 replica 数量是否达到最小副本要求来改变相应 block 状态为 COMPLETE。
Pipeline recovery 可以发生在这三个阶段中的任意一个,只要在写入过程中一个或多个 DataNode 遭遇网络或自身故障。我们来分别分析下。
( 1 )从 pipeline setup 错误中恢复
在 pipeline 准备阶段发生错误,分两种情况:
- 新写文件:Client 重新请求 NameNode 分配 block 和 DataNodes,重新设置 pipeline。
- 追加文件:Client 从 pipeline 中移除出错的 DataNode,然后继续。
( 2 )从 data streaming 错误中恢复
- 当 pipeline 中的某个 DataNode 检测到写入磁盘出错(可能是磁盘故障),它自动退出 pipeline,关闭相关的 TCP 连接。
- 当 Client 检测到 pipeline 有 DataNode 出错,先停止发送数据,并基于剩下正常的 DataNode 重新构建 pipeline 再继续发送数据。
- Client 恢复发送数据后,从没有收到确认的 packet 开始重发,其中有些 packet 前面的 DataNode 可能已经收过了,则忽略存储过程直接传递到下游节点。
(3)从 close 错误中恢复
到了 close 阶段才出错,实际数据已经全部写入了 DataNodes 中,所以影响很小了。
Client 依然根据剩下正常的 DataNode 重建 pipeline,让剩下的 DataNode 继续完成 close 阶段需要做的工作。
以上就是 pipeline recovery 三个阶段的处理过程,这里还有点小小的细节可说。
当 pipeline 中一个 DataNode 挂了,Client 重建 pipeline 时是可以移除挂了的 DataNode,也可以使用新的 DataNode 来替换。这里有策略是可配置的,称为 DataNode Replacement Policy upon Failure,包括下面几种情况:
- NEVER:从不替换,针对 Client 的行为
- DISABLE:禁止替换,DataNode 服务端抛出异常,表现行为类似 Client 的 NEVER 策略
- DEFAULT:默认根据副本数要求来决定,简单来说若配置的副本数为 3,如果坏了 2 个 DataNode,则会替换,否则不替换
- ALWAYS:总是替换
3.2 读操作
读操作就简单一些了,如图所示,client要从datanode上读取FileA。而FileA由block1和block2组成。
那么,读操作流程为:
a. client向namenode发送读请求。
b. namenode查看Metadata信息,返回fileA的block的位置。
block1:host2,host1,host3
block2:host7,host8,host4
c. block的位置是有先后顺序的,先读block1,再读block2。而且block1去host2上读取;然后block2,去host7上读取;
上面例子中,client位于机架外,那么如果client位于机架内某个DataNode上,例如,client是host6。那么读取的时候,遵循的规律是:
优选读取本机架上的数据。
运算和存储在同一个服务器中,每一个服务器都可以是本地服务器
补充说明:
元数据
元数据被定义为:描述数据的数据,对数据及信息资源的描述性信息。(类似于Linux中的i节点)
以 “blk_”开头的文件就是 存储数据的block。这里的命名是有规律的,除了block文件外,还有后 缀是“meta”的文件 ,这是block的源数据文件,存放一些元数据信息。
数据复制
NameNode做出关于块复制的所有决定。它周期性地从集群中的每个DataNode接收到一个心跳和一个block报告。收到心跳意味着DataNode正常运行。Blockreport包含DataNode上所有块的列表。
4. HDFS与Zookeeper(待完善)
5. HDFS shell