打怪升级之小白的大数据之旅(四十六)
HDFS各模块的原理
上次回顾
上一章,我们学习了HDFS的基本知识以及一些常用的操作,本章,我们对HDFS各模块的原理进行讲解,了解清楚这些,可以更好的辅助我们理解HDFS
HDFS的数据流
HDFS是以流的方式对数据进行存储与读取的,下面我们就根据它的底层原理来认识HDFS的写入与读取逻辑
HDFS写数据流程
写数据的流程分为三块:文件写入、网络拓扑以及机架感知,下面我来详细为大家介绍
HDFS文件写入的原理
首先,我先放一个整体的写数据流程图
- 客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
- NameNode返回是否可以上传。
- 客户端请求第一个 Block上传到哪几个DataNode服务器上。
- NameNode返回3个DataNode节点,分别为dn1、dn2、dn3。
- 客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。
- dn1、dn2、dn3逐级应答客户端。
- 客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。
- 当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)
接下来我就使用通俗的语言来解释这个原理:
-
首先我有一个客户端,一个NameNode,以及三台存数据的节点DataNode,还有我要上传到HDFS的数据 ss.avi,它的大小是200MB,所以我们的这个ss.avi文件就会被切割为2个块,分别是128m和72m,我们要知道,数据的传输是单线程的,当第一个块传输完毕才会传输第二个块的
-
客户端通过DistributedFileSystem这个模块向NameNode请求需要上传文件
-
NameNode会翻看一下自己的目录(元数据),看看里面有没有ss.avi这个文件,发现没有,于是告诉客户端,可以进行数据的上传。
-
客户端收到消息后,进行切割操作,将数据切割成两块,然后请求上传第一个块(block)数据,并询问NameNode,它需要向上传到哪几个DataNode服务器上
-
NameNode将可以上传的数据节点dn1,dn2,dn3告诉客户端
-
客户端开始通过FSDataOutputStream模块进行数据的上传第一个块(block)前,它会向DataNode1请求,建立块(block的传输通道),然后将数据从磁盘中读取放到本地内存中进行缓存
-
与DataNode1的传输通道建立后,DataNode1就会向它后面的节点DataNode2进行请求通道建立,DataNode2会向后面的DataNode3请求建立通道
-
DataNode3会告诉DataNode2,通道建立成功,DataNode2会告诉DataNode1,通道建立成功,DataNode1会告诉客户端通道建立成功
-
客户端收到应答后,就会将块数据以包(Packet)的形式进行传输(就像图中的小圆点)
-
每一个数据包在到达节点时会进行校验(Byetbuffer),查看是否丢失数据(丢包)
-
确认数据包正常时就会写入到DataNode1的磁盘中(这个过程叫落盘),然后这个数据包通过前面建立的通道进入下一个节点…直到整个块的数据传输完成
-
当第一个块数据传输完成后,就会重复上面的动作继续进行第二个块的传输
-
整个数据传输完成后,客户端会告诉NameNode,然后关闭资源
好了,这就是整个的HDFS的文件存储逻辑,我们需要的注意以下几点:
- 客户端只会向一个节点进行通道的建立,即我上面所演示的DataNode1,其他的通道建立是由HDFS自己建立的
- 为什么要建立三台节点?因为我们前面学习了,HDFS默认是建立了三个副本,所以我们在传输数据时,建立了通道,通过数据包落盘的方式,可以完成数据的副本操作
网络拓扑-节点距离计算原理
- 在上面的数据写入流程中,不知道大家有没有一个疑问:前面我的示例中,为什么不是直接与中间的节点建立通道而是与第一个dn1建立通道呢?
- NameNode会选择距离待上传数据最近距离的DataNode接收数据。那么这个最近距离怎么计算呢?
- 这里有一个公式:节点距离=两个节点到达最近的共同祖先的距离总和
- 举个例子来说明:我有一个数据中心,里面有两个集群,d1和d2,另外每个节点有三个机架,里面存放了三个节点(服务器)
- 我们知道集群、节点之间的连接都是通过网络进行通信的(交换机或者路由器来实现),所以几点距离选择最近的原因就距离越短,它所消耗的网络资源就越少
找节点的方式举例: - 同一节点上的进程之间寻找
- 同一个节点中,找到自己的其他进程,直接通过网络端口号就可以找到
(d1/r1/n0,/d1/r1/n0)=0
- 同一机架上的不同节点
- 当n-0找自己机架上的n-1节点时,首先,它会先找自己机架的交换机/路由器,然后通过它寻找n-1节点,所以,n-0到n-1节点的距离就是
(d1/r1/n0,d1/r1/n1)=2
- 同一集群中不同机架上的节点
- 首先机架会先通过机架上的路由找到它的集群路由,然后通过集群路由找到所需要找寻的机架路由,再通过机架的路由找到所需要的节点
(d1/r2/n0,d1/r3/n2)=4
- 不同集群中的节点
- 就和上面的逻辑一样,不同集群中的节点链接,需要先找到根的路由,然后再找到它下面的路由,最后找到对应的节点
(d1/r2/n1,d2/r4/n1)=6
HDFS的机架感知原理(副本存储的节点选择)
机架感知的官方的说明:http://hadoop.apache.org/docs/r3.1.3/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html#Data_Replication
举个例子,我有一个集群d1,里面有三个机架,每个机架有三个节点
副本的存储有两种情况
- 客户端不在我们的集群节点中、
- 客户端不在我们的集群节点中,副本是随机进行选择的
- 客户端在我们的集群节点中
- 客户端在我们的集群节点中时,第一个副本在客户端(client)所处的节点上
- 第二个副本在最近的一个机架的随机一个节点上
- 第三个副本在第二个副本所在的机架的随机节点上
- 大家不免会有疑问,为什么第三个副本不在第三个机架上呢?这是因为第一个副本所在的机架离第三个机架的距离太远了。在前面我们刚学习了网络拓扑,每一个节点间的连接都需要占用网络资源
- 那么为什么三个副本都不在第一个机架上呢?很简单,假设我们存储第一个副本的机架r1坏掉了,那么数据就没了,所以为了安全考虑就这么选择的
HDFS读数据流程
同样的,我先放一个读数据的整体流程,示例还是以前面存储的ss.avi举例
- 客户端通过Distributed FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
- 挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
- DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
- 客户端以Packet为单位接收,先在本地缓存,然后写入目标文件
跟写数据一样,下面来详细解释一下读数据的流程
- 首先我有一个客户端,一个NameNode,三个节点;NameNode中存储了前面我们写入的数据ss.avi的元数据(三个副本,两个块,文件的名称、大小等信息)
- 客户端创建一个DistributedFileSystem流,向NameNode发送请求,需要下载ss.avi文件,NameNode接收请求后,会发送该文件的元数据
- 客户端接收到元数据后,知道了这个数据有两块,接着它会创建FSDataInputStream流,用于读取数据,然后它会根据我们前面学到的网络拓扑以及机架感知就近的原则,随机选择一个节点请求数据的第一个块
- 该节点会将块数据进行传输,同样的,使用的是数据包方式进行传输
- 客户端在接收过程中也会进行Byetbuffer校验,查看是否丢包,确认数据无误,然后进行落盘
- 传输完成了第一个块数据后,再次请求第二个块数据,同样是随机选择一个节点,该节点接到请求后进行第二个块的数据传输
- 两个数据块都接收完成后,客户端会将数据进行拼接,然后关闭资源
NameNode和SecondaryNameNode
NN和2NN工作机制
NN和2NN的关系
- 我们前面写入、读取数据的都讲了,接下来就是介绍NameNode和SecondaryNameNode的原理了,在前面的数据传输过程中,不知道大家有没有想过一个问题:NameNode的元数据是存储在哪里的?
- 假设元数据存储在NameNode所在的节点磁盘中呢?我们知道,磁盘的读写远远不如内存,存储在磁盘中必然效率很低
- 那么元数据存储在内存中呢?内存虽然运行速度快,但是它无法持久保存,如果断电或其他情况导致节点或集群关闭,那么元数据就丢失了,所以它会在运行过程中,将产生过的数据写入进磁盘中,这就是备份元数据的FsImage,但内存中的数据并不能实时进行备份,实时备份太占用资源了
- HDFS为了解决这个问题,引入Edits文件(只进行追加操作,效率很高)。每当元数据有更新或者添加元数据时,修改内存中的元数据并追加到Edits中。这样,一旦NameNode节点断电,可以通过FsImage和Edits的合并,合成元数据
- 它所产生的问题大家想必知道了,长时间添加数据到Edits中,文件数据会很大,效率就会降低,如果断电等情况需要数据恢复时。就会特别的慢
- 能不能定期进行Edit与fsimage进行合并呢?这样问题不就解决了,但这个功能不能让NameNode来做,这样效率太低了,于是NameNode雇佣了一个秘书,它就是2NN节点(SecondaryNameNode)
- 还记得我们集群搭建时说过,NameNode不要和SecondaryNameNode在同一个节点上部署么,这就是原因,我们将SecondaryNameNode单独放到一个节点中。这样就可以大大提高HDFS的工作效率
NN和2NN的工作原理
第一阶段:NameNode启动
- 第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
- 客户端对元数据进行增删改的请求。
- NameNode记录操作日志,更新滚动日志。
- NameNode在内存中对元数据进行增删改。
第二阶段:Secondary NameNode工作
- Secondary NameNode询问NameNode是否需要CheckPoint。直接带回NameNode是否检查结果。
- Secondary NameNode请求执行CheckPoint。
- NameNode滚动正在写的Edits日志。
- 将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode。
- Secondary NameNode加载编辑日志和镜像文件到内存,并合并。
- 生成新的镜像文件fsimage.chkpoint。
- 拷贝fsimage.chkpoint到NameNode。
- NameNode将fsimage.chkpoint重新命名成fsimage。
Fsimage与Edits工作原理
工作原理
还记得当我们第一次部署集群时,需要格式化NameNode么?这个操作就是为了产生Fsimage与Edits文件
- Fsimage文件是HDFS文件系统元数据中的一个永久性的检查点,里面包含了HDFS文件系统的所有目录和文件inode的序列化信息
- Edits文件用于存放HDFS文件系统的所有更新操作的路径(命令),文件系统客户端执行的所有写的操作会被记录到Edits文件中
- 因为我们在学习IO流的时候知道,Java对象的保存需要进行序列化,而Hadoop是Java写的,所以为什么存储序列化信息大家知道了吧
Fsimage与Edits的文件内容
查看Fsimage与Edits文件,我们进入到如下目录中
cd /opt/module/hadoop-3.1.3/data/dfs/name/current/
因为我前面有很多操作,所以有很多的Edites文件,前面我们说了,它们是以序列化的形式进行保存的,那么我们怎么查看它的文件内容呢?我们可以使用如下命令
oiv查看Fsimage文件
- 语法格式
hdfs oiv -p 文件类型 -i镜像文件 -o 转换后文件输出路径
- 示例
# 将fsimage_0000000000000000225文件转换为xml文件并设置名称为fsimage.xml hdfs oiv -p XML -i fsimage_0000000000000000225 -o /opt/module/hadoop-3.1.3/fsimage.xml
- 通过查看文件,我们会发现文件中的内容没有记录块所对应DataNode,这是因为DataNode节点会间隔一段时间向NameNode汇报一次数据块信息
oev查看Edits文件
- 语法格式
hdfs oev -p 文件类型 -i编辑日志 -o 转换后文件输出路径```
- 示例
hdfs oev -p XML -i edits_0000000000000000012-0000000000000000013 -o /opt/module/hadoop-3.1.3/edits.xml
- NameNode如何确定下次启动时合并哪些Edits?很简单
seen_txid记录的是edits后面的号码,inprogress_xx是当前执行的edits文件,我们可以根据这两个信息来确定下次启动时合并哪些Edits文件
CheckPoint时间设置
- 通常情况下,SecondaryNameNode每隔一小时执行一次
- SecondaryNameNode会一分钟检查一次操作次数,当操作次数达到1百万时,SecondaryNameNode执行一次
- 这个设置在hdfs-default.xml中,因此我们可以自己来更改CheckPoint的时间设置,但是不建议,因为没有必要,使用默认就好
DataNode
DataNode工作原理
接下来就是最后一个模块:DataNode,还是老样子,直接上整个流程图
- 一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
- DataNode启动后向NameNode注册,通过后,周期性(1小时)的向NameNode上报所有的块信息。
- 心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用。
- 集群运行中可以安全加入和退出一些机器
详细流程如下:
- NameNode是老大,它管理着所有的DataNode,每一个DataNode中都是以数据块(block)的形式进行数据的存储,数据块中除了数据本身,还有数据的长度、校验和以及时间戳
- 校验和就是为了防止数据被篡改,后面介绍数据完整性时,会介绍校验和
- 当DataNode启动时,会向老大汇报自身情况即它的块数据,这就是向NameNode注册,我们可以理解为上班签到,老大点头后告诉DataNode成功后,DataNode就会正式开始工作
- 在它工作时,会以每个小时为一个周期再次上报所有的块信息,老大就会根据汇报结果进行元数据的更新等操作
- 在这一小时周期中,DataNode节点可能会因各种情况导致节点崩溃,那么老大NameNode怎么知道该节点是否正常工作呢?
- 此时就要引入一个知识点–心跳,DataNode每3秒会告诉NameNode老大,自己还活着…如果老大有新的指令,就会带着指令进行操作(所以我们可以进行数据的操作)
- 如果某个节点超过十分钟都没有向NameNode老大汇报心跳,那么老大就会认为这个DataNode小弟挂掉了,如果此时有备用的节点,就会替换掉挂掉的小弟
掉线时限参数设置
-
前面我们说了,DataNode会周期性的汇报工作和心跳,它同样是以配置文件的形式来设置的,设置的配置文件为:
hdfs-site.xml
-
我们要注意一下:
heartbeat.recheck.interval
的单位为毫秒,dfs.heartbeat.interval
的单位为秒<property> // 汇报的周期 <name>dfs.namenode.heartbeat.recheck-interval</name> <value>300000</value> </property> <property> // 心跳的秒数 <name>dfs.heartbeat.interval</name> <value>3</value> </property>
数据完整性
- 在我现实生活中,如果某个交通要道的红绿灯信号一直是绿灯,或者一直是红灯,岂不是乱了套了,同样的,如果DataNode节点的数据损坏了,却没有发现,那岂不是很危险,因此我们使用校验和机制来保证数据的完整性
- 当DataNode读取Block的时候,它会计算CheckSum。
- 如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏。
- Client读取其他DataNode上的Block。
- DataNode在其文件创建后周期验证CheckSum
总结
本章对HDFS的各个模块的底层原理进行了讲解,这个内容主要是为了辅助我们理解HDFS,下一章我为大家带来工作中会遇到的一些HDFS的扩展知识