目录
3.5.4、Federation HDFS与当前HDFS的比较及改进
一、概述
HDFS集群由NameNode、DataNode、Secondary NameNode和Client组成。
NameNode是集群的管理节点,主要负责管理HDFS的文件元数据、配置副本策略、处理客户端的读写请求。
DataNode 负责集群的数据存储以及执行客户端的读写请求。
Secondary NameNode是NameNode 的辅助节点,其作用是辅助NameNode,分担NameNode的工作压力;定期合并Fsimage和Edits,并且同步给NameNode;紧急情况下,可以辅助恢复NameNode。
Client 是HDFS的客户端,和NameNode交互,获取数据的映射信息;和DataNode交互,负责数据的读写流程;同时还负责文件的切分工作,文件在上传HDFS的时候,Client会将文件切分成一个一个的Block,按顺序读到本地缓存,然后上传HDFS。
二、HDFS核心设计
2.1、HDFS心跳机制
心跳机制主要包含两方面:心跳和检查
具体流程如下:
1、DataNode会定时向NameNode发送心跳数据包做汇报,默认是3秒发送一次;
2、当DataNode长时间没有向NameNode发送心跳时,NameNode会启动检查机制,检查DataNode的状态
3、当DataNode停止发送心跳时间超过默认值后,HDFS会将该DataNode节点状态改为 dead。默认超时计算规则如下
timeout = 2 * heartbeat.recheck.interval + 10 * dfs.heartbeat.interval
默认的检查时间 heartbeat.recheck.interval = 5分钟,dfs.heartbeat.interval是3秒。不过默认时间是可以修改的,在 hdfs-site.xml 文件中修改,值得注意的是在 hdfs-site.xml 中heartbeat.recheck.interval 单位是 毫秒,而dfs.heartbeat.interval单位是 秒。所以,举个例子,如果 heartbeat.recheck.interval设置为5000(毫秒),dfs.heartbeat.interval 设置为3(秒),那么总的超时时间是40秒。
<property>
<name>heartbeat.recheck.interval</name>
<value>5000</value>
</property>
<property>
<name>dfs.heartbeat.interval</name>
<value>3</value>
</property
2.2、HDFS安全模式(SafeMode)
SafeMode是NameNode的状态之一(active、standby、safeMode)
2.2.1、NameNode进入SafeMode 的方式
1、集群刚启动时
由于HDFS集群的启动顺序是NameNode、DataNode、Secondary NameNode,所以当NameNode启动时,DataNode还没有启动,NameNode没有收到所有DataNode的心跳汇报,所以NameNode会进入安全模式。正常启动时进入SafeMode的原理如下
1 、 NameNode 的内存元数据中,包含文件存储目录的路径、副本数、 blockid ,及每一个 block 所在 DataNode的信息,而 fsimage 中,不包含 block 所在的 DataNode 信息2 、当 NameNode 冷启动时,此时内存中的元数据只能从 fsimage 中加载而来,从而就没有 block 所在的DataNode信息,就会导致 NameNode 认为所有的 block 都已经丢失3 、从而 HDFS 会自动进入安全模式4 、伴随着每个 DataNode 启动后,会定期向 NameNode 汇报自身所持有的 blockid 信息,随着 DataNode 陆续 启动,从而陆续汇报block 信息, NameNode 就会将内存元数据中的 block 所在 DataNode 信息补全更新5 、当 HDFS 集群中的每个文件都找到了所有 block 的位置,从而自动退出安全模式
2、当HDFS数据丢失到一定程度时
当NameNode发现集群中的数据丢失到一定程度时(默认 0.1%),NameNode就会进入安全模式,进入安全模式后,客户端就不能再进行元数据的修改操作,只能进行元数据的查询操作。
这个丢失率是可以手动配置的。
# 默认是
dfs.safemode.threshold.pct=0.999f
# 新版本的配置方式
dfs.namenode.safemode.threshold-pct=0.999f
2.2.2、如何退出SafeMode
1、找到问题所在,进行修复(比如修复宕机的DataNode)
2、强制手动退出安全模式(但是并没有解决数据丢失问题)
注意:正常启动时候进入安全模式,不需要去理会,等集群正常启动之后,NameNode会自动退出安全模式。
2.2.3 安全模式常用命令总结
hdfs dfsadmin -safemode leave //强制NameNode退出安全模式
hdfs dfsadmin -safemode enter //进入安全模式
hdfs dfsadmin -safemode get //查看安全模式状态
hdfs dfsadmin -safemode wait //等待,一直到安全模式结束
2.3、副本存放策略及机架感知
2.3.1 原理
首先,HDFS对文件是分块存储。每个块的副本存放一般默认是存放3份,这些副本块存储在不同的节点上,这种分块存储+副本的策略是保证HDFS可靠性和性能的关键,其原因如下:
1、文件分块之后,按照数据块来读,提高了文件随机读和并发读的效率;
2、保存若干副本到不同节点,实现可靠性的同时也提高了同一数据块并发读的效率
3、分块存储非常切合MR的任务切分的思想。在这里,副本的存放策略又是HDFS实现高可靠和高性能的关键。
HDFS采用一种 机架感知 的策略来改进数据的可靠性、可用性和网络带宽的利用率。
副本存放策略:客户端放一份,客户端所在机架别的节点存一份,其他机架的节点存一份(就近原则,网络拓扑距离),在这种情况下,数据并不是均衡的存储在所有节点上:而是三分之一的数据在一个节点(客户端节点)上,三分之二的数据在同一个机架(客户端所在机架)上,剩下的三分之一副本均匀的分布在其他的机架上。这种策略在不损害高可靠性和读取性能的情况下,提高了数据的写的性能。
2.3.2、总结
1、作用
数据分块存储和副本的策略,是保证高可靠性和性能的关键
2、方法
将每个文件的数据进行分块存储,每一个数据块保存多个副本,每个副本在不同的节点上。
3、存放说明
多数情况下,HDFS默认存储三个副本,其存放策略如下:如图Block1、Block2分别表示两个数据块,Block1的三个副本分别存储在Rack1的dn1、dn2和Rack2 的dn1节点中,block2 的三个副本分别存储在 Rack1 的dn1、dn3和Rack3的 dn2中。

4、修改副本数
修改副本数有两种方法,一种是修改hdfs-site.xml文件,这种方法只会作用于将来上传到HDFS的文件;另一种是通过命令的方式来修改指定目录下已经存在的文件,无法对将来上传的文件进行影响。两种设置方式如下:
# 修改 hdfs-site.xml 文件
<property>
<name>dfs.replication</name>
<value>2</value>
</property>
# 命令行设置
hadoop fs -setrep -R 2 /
2.4、负载均衡
HDFS分块存储+副本策略,虽然保证了数据的高可靠性和高性能,但是也有一些弊端,就是数据分布不均匀,三分之二的数据都是集中在一个机架上,针对这种情况,HDFS提供了负载均衡的操作,只不过这个负载均衡的效果比较慢。
数据均衡需要满足以下原则:
1、数据均衡不能导致数据块减少,数据块副本丢失
2、管理员可以中止均衡进程
3、每次移动的数据量及占用的网络资源,必须是可控的
4、数据均衡过程,不能影响NameNode正常工作
HDFS负载均衡 机器容量最高的值和最低的值 差距不能超过10%。
均衡命令如下:
# 默认均衡命令
sbin/start-balancer.sh
# 指定差距
sbin/start-balancer.sh -threshold 5
sbin/start-balancer.sh -t 10%
HDFS负载均衡的命令类似于 java垃圾回收中的 System.gc()方法,不会立即响应,只会在空闲的时候进行操作。
自动均衡进行的过程是非常缓慢的,因为 HDFS集群默认balance操作不能占据太大的网络带宽,当然,这个带宽也是可以调节的。调节命令如下:
# 这里举例配置的是10M/s,默认是1M/s
hdfs dfsadmin -setBalanacerBandwidth newbandwidth
hdfs dfsadmin -setBalanacerBandwidth 10485760
另外,也可以直接在 hdfs-site.xml中直接修改
<property>
<name>dfs.balance.bandwidthPerSec</name>
<value>10485760</value>
<description> Specifies the maximum bandwidth that each datanode can utilize for the balancing purpose in term of the number of bytes per second.
</description>
</property>
三、HDFS 工作机制
3.1、HDFS的写流程
该机制讲述一个大文件,该如何被写入到HDFS。
3.1.1、流程概述
客户端要向HDFS写数据,首先要向NameNode通信以确认数据可以写入,并确认block写入的DataNode,然后客户端逐一将block传给对应的DataNode,并由接收到bolck的DataNode节点负责向其他DataNode复制block副本。具体流程如下:
1、使用HDFS提供的客户端client向远程的NameNode发起RPC请求。
2、NameNode检查药创建的文件是否存在,创建是否有权限进行操作,成功则会为文件创建一条记录,否则会让客户端抛出异常。
3、当客户端开始写入数据的时候,客户端会将文件分成多个 packets ,并在内部以数据队列“data queue(数据队列)”的形式管理这些pockets,并向NameNode申请blocks,获取用来存储replicas的合适的DataNode列表,列表的大小由NameNode中的replication的设定而定。
4、开始以pipeline(管道)的形式将packet写入所有的replicas中。客户端把packet以流的方式写入第一个DataNode,该DM在存储该packet之后,将其传给该pipeline中的下一个DataNode,直到最后一个DataNode。这种写数据的方式呈流水线式的形式。
5、最后一个DataNode存储成功之后,会返回一个 “ ack packet(确认队列)”,在pipeline中传递到客户端,在客户端的开发库内部维护着“ ack queue”,当成功的收到DataNode返回的 ack packet后会从 “ ack queue”中删除对应的packet。
6、如果在传输过程中,某个DataNode发生故障,那么当前的pipeline会关闭,出现故障的DataNode会从当前的pipeline中移除,剩余的block会继续在剩余的DataNode中继续以pipeline的形式传输,同时NameNode会安排一个新的DataNode到pipeline中,保持replicas的数量。
7、客户端完成数据的写入后,会对数据流调用 close()方法,关闭数据流。
扩展:
只要写入了 dfs.replication.min(最小写入成功的副本数)的副本数(默认为1),写操作就会成功,并且这个数据块可以在集群中异步复制,直到达到其目标副本数(hdf.replication的默认数是3),因为NameNode已经知道文件由哪些块组成,所以它在返回成功前只需要等待数据块进行最小量的复制。
3.1.2、步骤图详解

3.1.3、详细文字说明(大白话图解)
1、客户端通过调用DistributedFileSystem的create方法,创建一个新的文件
2、DistributedFileSystem通过RPC(远程过程调用)调用NameNode,去创建一个没有 blocks关联的新文件。创建前NameNode会做各种校验,比如文件是否存在,客户端有无权限去创建等。如果校验通过,NameNode 就会记录下新文件,否则就会抛出IO异常。
3、前两步结束后会返回 FSDataOutputStream 的对象,和读文件的时候相似,FSDataOutputStream 被封装成 DFSOutputStream,DFSOutputStream 可以协NameNode 和 DataNode。客户端开始写 数据到DFSOutputStream,DFSOutputStream 会把数据切成一个个小 packet,然后排成队列 data queue。
4、DataStreamer 会去处理接受 data queue,它先问询 NameNode 这个新的 block 最适合存储的在 哪几个DataNode里,比如重复数是3,那么就找到3个最适合的 DataNode,把它们排成一个 pipeline。DataStreamer 把 packet 按队列输出到管道的第一个 DataNode 中,第一个 DataNode 又 把 packet 输出到第二个 DataNode 中,以此类推。
5 、 DFSOutputStream 还有一个队列叫 ack queue ,也是由 packet 组成,等待 DataNode 的收到响应,当pipeline 中的所有 DataNode 都表示已经收到的时候,这时 ack queue 才会把对应的 packet 包移除掉。6 、客户端完成写数据后,调用 close 方法关闭写入流。7 、 DataStreamer 把剩余的包都刷到 pipeline 里,然后等待 ack 信息,收到最后一个 ack 后,通知DataNode 把文件标示为已完成。
3.2、HDFS的读流程
该机制讲述是的如何从HDFS读到一个文件。
3.2.1、流程概述
1 、使用 HDFS 提供的客户端 Client ,向远程的 NameNode 发起 RPC 请求;2 、 NameNode 会视情况返回文件的全部 block 列表,对于每个 block , NameNode 都会返回有该block 拷贝的 DataNode 地址;3 、客户端 Client 会选取离客户端最近的 DataNode 来读取 block ;如果客户端本身就是 DataNode ,那么将从本地直接获取数据;4 、读取完当前 block 的数据后,关闭当前的 DataNode 链接,并为读取下一个 block 寻找最佳的DataNode;5 、当读完列表 block 后,且文件读取还没有结束,客户端会继续向 NameNode 获取下一批的 block 列表;6 、读取完一个 block 都会进行 checksum 验证,如果读取 DataNode 时出现错误,客户端会通知 NameNode,然后再从下一个拥有该 block 拷贝的 DataNode 继续读
3.2.2、步骤图详解

3.2.3、详细文字说明
1 、客户端首先调用 FileSystem 对象的 open 方法打开文件,其实获取的是一个DistributedFileSystem 的实例。2 、 DistributedFileSystem 通过调用 RPC ( 远程过程调用 ) 向 namenode 发起请求,获得文件的第一批 block 的位置信息。同一 block 按照备份数会返回多个 DataNode 的位置信息,并根据集群的网络拓扑 结构排序,距离客户端近的排在前面, 如果客户端本身就是该 DataNode ,那么它将从本地读取文件。3 、 DistributedFileSystem 类返回一个 FSDataInputStream 对象给客户端,用来读取数据,该对象会被封装成 DFSInputStream 对象,该 DFSInputStream 对象管理着 datanode 和 namenode 的 I/O 数据流。客户端对输入端调用 read 方法, DFSInputStream 就会找出离客户端最近的 datanode 并连接datanode。4 、在数据流中重复调用 read() 函数,直到这个块全部读完为止。 DFSInputStream 关闭和此DataNode 的连接。接着读取下一个 block 块。这些操作对客户端来说是透明的,从客户端的角度来看只是读一个持续不断的流。每读取完一个 block 都会进行 checksum 验证,如果读取 datanode 时出现错误,客户端会通知 NameNode ,然后再从下一个拥有该 block 拷贝的 datanode 继续读。5 、当正确读取完当前 block 的数据后,关闭当前的 DataNode 链接,并为读取下一个 block 寻找最佳的DataNode 。如果第一批 block 都读完了,且文件读取还没有结束 DFSInputStream 就会去namenode 拿下一批 block 的位置信息继续读。6 、当客户端读取完毕数据的时候,调用 FSDataInputStream 的 close 方法关闭掉所有的流。
3.3、NameNode的工作机制
3.3.1、NameNode职责
HDFS是主从架构,NameNode是HDFS的主节点,负责管理集群。其职责具体为以下几点:
1、负责客户端请求(读写数据请求)的响应
2、管理HDFS的元数据:包括命名空间、访问控制信息、文件与数据块的映射信息和数据块的存储位置等
3、管理应用和副本存放策略
4、管理集群数据块的负载均衡问题
3.3.2、NameNode元数据存储机制及管理
WAL ( Write ahead Log ) : 预写日志系统在计算机科学中,预写式日志( Write-ahead logging ,缩写 WAL )是关系数据库系统中用于提供原子 性和持久性(ACID 属性中的两个)的一系列技术。在使用 WAL 的系统中,所有的修改在提交之前都要 先写入 log 文件中。Log 文件中通常包括 redo 和 undo 信息。这样做的目的可以通过一个例子来说明。假设一个程序在执 行某些操作的过程中机器掉电了。在重新启动时,程序可能需要知道当时执行的操作是成功了还是部分成功或者是失败了。如果使用了 WAL ,程序就可以检查 log 文件,并对突然掉电时计划执行的操作内容跟实际上执行的操作内容进行比较。在这个比较的基础上,程序就可以决定是撤销已做的操作还是继续完成已做的操作,或者是保持原样。WAL 允许用 in-place 方式更新数据库。另一种用来实现原子更新的方法是 shadow paging ,它并不是 in-place 方式。用 in-place 方式做更新的主要优点是减少索引和块列表的修改。 ARIES 是 WAL 系列技术 常用的算法。在文件系统中,WAL 通常称为 journaling PostgreSQL 也是用 WAL 来提供 point-intime 恢复和数据库复制特性。
NameNode 对元数据的存储采用两种存储方式:内存和磁盘
1、内存metadata
在内存中存储了一份完整的元数据,包括目录树结构,以及文件和数据块和副本存储地的映射关系;为了提高读写响应。
2、磁盘元数据
在磁盘中也存储了一份完整的元数据,为了提高数据安全性。
磁盘中的元数据由以下几部分组成:
fsimage 镜像文件
edits 历史元数据编辑日志
edits_inprogress 正在使用的编辑日志,只有一个
VERSION 记录元数据的版本信息
seen_txid checkpoint过程中记录的上一次chekpoint的TXID
注意:磁盘元数据,存储在namenode节点的某个指定目录下,安装的时候有制定过

上图中的磁盘元数据镜像文件fsimage_0000000000024994452
fsimage_0000000000024994452
等价于
edits_0000000000000000001-0000000000000000118
...
edits_0000000000024991477-0000000000024994452
合并之和。
也就是说
完整的元数据metadata = 最新的 fsimage_0000000000024995189 + edits_inprogress_0000000000024995190
完整的元数据metadata = 所有的edits之和(edits_001_002 + …… + edits_444_555 +
edits_inprogress_0000000000024995190 )完整的元数据metadata = 内存元数据
#Fri May 07 10:31:34 CST 2021
namespaceID=896744018
clusterID=CID-cfbc9ac6-8814-46d3-a713-d57b3292458f
cTime=1590433690859
storageType=NAME_NODE
blockpoolID=BP-908665078-192.168.100.191-1590433690859
layoutVersion=-64
当然,edits和fsimage文件也都有特殊的命令可以查看,命令如下
# 查看edits文件
hdfs oev -i edits_0000000000000000193-0000000000000000194 -o edits.xml
cat edits.xml
# 查看fsimage镜像文件信息
hdfs oiv -i fsimage_0000000000000000144 -p XML -o fsimage.xml
cat fsimage.xml
3.3.3、元数据的Checkporint
每隔一段时间,secondary NameNode会将NameNode上累计的edits 和一个最新的 fsimage 文件下载到本地,并加载到内存进行合并 (这个过程称为 checkporint)
checkporint详细过程图解如下:

checkporint详细过程文字描述:
1 、周期性发送请求给 NN ,获取 fsimage 和 edits ;2 、 NN 收到请求后,生成一个空的 edits.new 文件3 、 NM 给 SNN 发送 fsimage 和 edits4 、 SNN 将 fsimage 文件加载到内存,合并 edits 文件5 、 snm 生成新的镜像文件 fsimage.ckpt6 、 SNN 发送 fsimage.ckpt 给 NN7 、 NN 将 fsimage.ckpt 替换 fsimage 文件,将 edits.new 重命名为 edits 文件
3.3.4、Checkporint的触发配置
##检查触发条件是否满足的频率,60秒
dfs.namenode.checkpoint.check.period=60
##以上两个参数做checkpoint操作时,secondary namenode的本地工作目录
dfs.namenode.checkpoint.dir=file://${hadoop.tmp.dir}/dfs/namesecondary
dfs.namenode.checkpoint.edits.dir=${dfs.namenode.checkpoint.dir}
##最大重试次数
dfs.namenode.checkpoint.max-retries=3
##两次checkpoint之间的时间间隔3600秒
dfs.namenode.checkpoint.period=3600
##两次checkpoint之间最大的操作记录
dfs.namenode.checkpoint.txns=1000000
3.3.5、Checkporint的附加作用
3.4、HDFS高可用机制
Hadoop 2.2.0 版本之前, NameNode 是 HDFS 集群的单点故障点,每一个集群只有一个 NameNode ,如果这个机器或者进程不可用,整个集群就无法使用,直到重启。Secondary NameNode 。它不是 HA ,它只是阶段性的合并 edits 和 fsimage ,以缩短集群启动的时间。 当NameNode( 以下简称 NN) 失效的时候, Secondary NN 并无法立刻提供服务, Secondary NN 甚至无 法保证数据完整性:如果NN 数据丢失的话,在上一次合并后的文件系统的改动会丢失。NameNode 或者新重启一个 NameNode 节点 。影响 HDFS 集群不可用主要包括以下两种情况:A :类似机器跌宕这样的意外情况将导致集群不可用,只有重启 NameNode 之后才可使用。B :计划内的软件或硬件升级( NameNode 节点)将导致集群在短时间范围内不可用。HDFS 的高可用性( HA , High Availability )就可以解决上述问题,通过提供选择运行在同一集群中的 一个热备用的 " 主 / 备 " 两个冗余 NameNode ,允许在机器宕机或系统维护的时候, 快速转移到另一个 NameNode。一个典型的 HA 集群,两个单独的机器配置为 NameNodes ,在任何时候,一个 NameNode 处于活动状态,另一个处于待机状态,活动的 NameNode 负责处理集群中所有客户端的操作,待机时仅仅作为一 个slave ,保持足够的状态,如果有必要提供一个快速的故障转移。为了保持备用节点与活动节点状态的同步,目前的实现需要两个节点同时访问一个共享存储设备(例如 从NASNFS 挂载)到一个目录。将有可能在未来的版本中放宽此限制。当活动节点对命名空间进行任何修改,它将把修改记录写到共享目录下的一个日志文件,备用节点会监 听这个目录,当发现更改时,它会把修改内容同步到自己的命名空间。备用节点 在故障转移时,它将 保证已经读取了所有共享目录内的更改记录,保证在发生故障前的状态 与活动节点保持完全一致。为了提供快速的故障转移,必须保证备用节点有最新的集群中块的位置信息,为了达到这一点,Datanode 节点需要配置两个 nameNode 的位置,同时发送块的位置信息和心跳信息到两个 nameNode。任何时候只有一个 namenode 处于活动状态,对于 HA 集群的操作是至关重要的,否则两个节点之间的状态就会产生冲突,数据丢失或其它不正确的结果,为了达到这个目的或者所谓的 “ 裂脑场景 ” 出现,管 理员必须为共享存储配置至少一个(fencing )方法。在宕机期间, 如果不能确定之间的活动节点已经 放弃活动状态,fencing 进程负责中断以前的活动节点编辑存储的共享访问。这可以防止任何进一步的 修改命名空间,允许新的活动节点安全地进行故障转移。
1 、只有一个 NameNode 是 Active 的,并且只有这个 Active NameNode 能提供服务,该 Active NameNode 宕机不可用的话,可以考虑让 Standby NameNode 切换成 Active 来提供服务。2 、提供手动 Failover ,在升级过程中, Failover 在 NameNode-DataNode 之间写不变的情况下才能生效。3 、在之前的 NameNode 重新恢复之后,不能提供 failback 。4 、数据一致性比 Failover 更重要。5 、尽量少用特殊的硬件。6 、 HA 的设置和 Failover 都应该保证在两者操作错误或者配置错误的时候,不得导致数据损坏。7 、 NameNode 的短期垃圾回收不应该触发 Failover 。8 、 DataNode 会同时向 Active NameNode 和 Standby NameNode 汇报块的信息。9 、 Active NameNode 和 Standby NameNode 通过 NFS 备份 MetaData 信息到一个磁盘上面。