0
点赞
收藏
分享

微信扫一扫

Hadoop | HDFS 学习笔记(三) | HDFS 读写流程 | NN、2NN、DN 工作机制 | FsImage与Edits的合并

静鸡鸡的JC 2022-01-20 阅读 28

文章目录

参考资料


  • 《大数据技术原理与应用(第3版)》——林子雨 版本

  • 视频资料

运行环境


  • JDK8
  • Hadoop3.1.3

一、HDFS 体系结构


HDFS采用了主从(Master/Slave)结构模型,一个HDFS集群包括一个名称节点(NameNode)和若干个数据节点(DataNode)
所有的HDFS通信协议都是构建在TCP/IP协议基础之上的
名称节点和数据节点之间则使用数据节点协议进行交互

在这里插入图片描述

二、HDFS 存储原理


2.1 冗余数据保存

HDFS采用多副本方式对数据进行冗余存储,通常一个数据块的多个副本会被分不到不同的DataNode节点。优点有:

  • 加快数据传输速度
  • 容易检查数据错误
  • 保证数据可靠性

2.2 数据存取策略

2.2.1 数据存放

第一个副本:放置在上传文件的数据节点,若是集群外提交,则随机挑选资源相对充足的节点
第二个副本:放置在与第一个副本不同的机架的节点上
第三个副本:防止在于第二个副本相同机架的其他节点上
更多副本:随机节点

2.2.2 数据读取

HDFS提供一个API可确定数据节点的机架ID,Client可以通过调用API查询。

Client读取HDFS数据时,从NN获得Block不同副本的存放位置列表,列表包含了副本所在的DN,然后可调用API确定Client和这些DN所属的机架ID

当发现某个Block副本的机架ID和Client对应的 机架ID相同时就优先选择该副本读取数据,若没有就随机选择一个副本。

2.3 数据错误与恢复

2.3.1 NN出错

NN存储了FsImageEditLog信息,若损坏则根据2NN中的FsImageEditlog数据进行恢复。

2.3.2 DN出错

  • 每个DN会定期向NN发送“心跳”信息,以汇报自身的状态
  • DN故障,会被标记为“宕机”,NN不会再发送I/O请求
  • NN会定期检查,防止出现Block的副本数小于冗余因子的情况,若发生就数据冗余复制,生成新副本。

HDFS与其他分布式文件系统最大区别就是:可调整冗余数据的位置。

2.3.3 数据出错

可能造成的原因: 网络传输问题 或 磁盘错误 等

Client读取数据后会采用 校验算法(crc、mdk5等)对文件进行校验,以确定读取到正确数据

Client 读取HDFS文件时,先读取信息文件,利用该文件对每个读取的Block进行校验

若出错,那么Client会请求到另一个DN读取该文件的Block,并且向NN报告该Block有错

NN会定期检查并重新复制这些 Block

三、HDFS 读写过程

通过HDFS Java API 的实现来理解HDFS读写流程,可参考文章 【Hadoop | HDFS 学习笔记(二)| HDFS Java API 环境搭建 | Java操作HDFS文件系统 | 多案例】

实现代码(uri为在HDFS需操作的文件路径):

Configuration conf = new Configuration();                          
conf.set("fs.defaultFS","hdfs://localhost:9000");                        
conf.set("fs.hdfs.impl","org.apache.hadoop.hdfs.DistributedFileSystem");
FileSystem fs = FileSystem.get(conf);
FSDataInputStream in = fs.open(new Path(uri));
FSDataOutputStream out = fs.create(new Path(uri));

首先了解这几个类的作用:

类名描述
FileSystem通用文件系统的抽象基类,可被分布式文件系统集成,支持Hadoop文件系统相关操作
DistrubutedFileSystem是FileSystem在HDFS文件系统中的具体实现类
FSDataInputStreamHDFS的输入流对象,用于获取HDFS文件的数据
FSDataOutputStreamHDFS的输出流对象,用于将数据写入到HDFS文件

在3.x版本中,输入输出流对象的使用都封装到了 org.apache.hadoop.fs.FileUtil里,比如在执行CopyFromLocalFile方法时,笔者通过调试发现了底层调用的部分:

在这里插入图片描述
在这里插入图片描述
通过调试能更好的理解这个调用的流程,不过由于目前的知识面比较浅,暂时只能粗略调试。

3.1 读流程

过程与分析

在这里插入图片描述

主要分为4个步骤:

  1. Client 通过 DistributedFileSystemNN 请求读取/下载文件,NN通过查询元数组,找到Block所在的DN地址
    2.Client 挑选一台DN服务器(就近原则,然后随机),请求读取数据
  2. DN 开始 传输数据给 Client(从磁盘读取数据输入流,以Packet为单位做校验)
  3. ClientPacket 为单位接收,先在本地缓存,然后写入目标文件。

3.2 写流程

3.2.1 过程与分析在这里插入图片描述

主要分为9个步骤:

  1. Client 通过 DistributeFileSystem类向 NN 请求上传文件,NN检查文件是否已存在,父目录是否存在
  2. NN响应此次请求,返回是否可以上传
  3. ClientNN 请求上传第一个 Block
  4. NN 根据选择策略(本机架当前节点 > 其他机架节点 > 本机架其他节点 > 随机节点) ,返回3个存储文件副本的DN节点,DN1、DN2、DN3
  5. 获取到DN节点后,Client通过 FSDataOutputStream 类向DN1请求上传数据,DN1收到请求后会继续调用DN2、DN2又调用DN3,建立成一个通信管道。
  6. DN1、DN2、DN3 逐级应答客户端
  7. 建立通信管道后,Client 开始往 DN1 上传 第一个Block,先从磁盘读取数据放到一个本地内存进行缓存,以 Packet为单位,DN1收到后就传给DN2、DN2收到后就传给DN3DN1每传一个Packet就会放入一个应答队列等待应答
  8. 当一个Block 传输完成之后,Client 再次请求 NN上传到下个Block的服务器,重复执行3-8部分
  9. 最后传输完毕,Client将处理的信息提交给 NN

3.2.2 网络拓扑-节点NN与DN之间的距离计算

在HDFS写数据过程,NN会选择距离待上传数据最近距离的DN接收,这个节点距离等于两个节点到达最近的共同祖先的距离总和。尚硅谷机构提供的这张图为例:
在这里插入图片描述
集群分为d1、d2,机架又分为r1、r2、r3、r4、r5、r6,而集群节点都称为n1、n2、n3。可以把它们看成是多叉树里的一个节点。

3.2.3 机架感知(副本存储节点选择)

首先回顾 HDFS的写流程,首先,Client向NN申请上传文件,NN检查文件用户是否有权限、然后再检查是否存在,若符合就响应Client提示允许上传,接着Client请求上传第一个Block,此时NN就需要根据一个策略来选择元数据里的DN负责存储这个副本了,在API中是在 BlockPlacementPolicyDefault类的chooseTargetInOrder方法实现的。
对应包的 Maven 依赖

<dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-hdfs</artifactId>
    <version>3.1.3</version>
</dependency>

在这里插入图片描述

    final int numOfResults = results.size();
    /** 第一个副本: 选择本地节点**/
    if (numOfResults == 0) {
      DatanodeStorageInfo storageInfo = chooseLocalStorage(writer,
          excludedNodes, blocksize, maxNodesPerRack, results, avoidStaleNodes,
          storageTypes, true);

      writer = (storageInfo != null) ? storageInfo.getDatanodeDescriptor()
                                     : null;
      
      if (--numOfReplicas == 0) {
        return writer;
      }
    }
  
    final DatanodeDescriptor dn0 = results.get(0).getDatanodeDescriptor();
     /**第二个副本: 选择其他机架的节点**/
    if (numOfResults <= 1) {
      chooseRemoteRack(1, dn0, excludedNodes, blocksize, maxNodesPerRack,
          results, avoidStaleNodes, storageTypes);
      if (--numOfReplicas == 0) {
        return writer;
      }
    }
    /**第三个副本: **/
    if (numOfResults <= 2) {
      final DatanodeDescriptor dn1 = results.get(1).getDatanodeDescriptor();
      /**若前两个副本在同一个机架,则选择其他机架**/
      if (clusterMap.isOnSameRack(dn0, dn1)) {
        chooseRemoteRack(1, dn0, excludedNodes, blocksize, maxNodesPerRack,
            results, avoidStaleNodes, storageTypes);
      } else if (newBlock){
        /** 若是新副本块,就选择第二个副本存储的机架 **/
        chooseLocalRack(dn1, excludedNodes, blocksize, maxNodesPerRack,
            results, avoidStaleNodes, storageTypes);
      } else {
        chooseLocalRack(writer, excludedNodes, blocksize, maxNodesPerRack,
            results, avoidStaleNodes, storageTypes);
      }
      if (--numOfReplicas == 0) {
        return writer;
      }
    }
    chooseRandom(numOfReplicas, NodeBase.ROOT, excludedNodes, blocksize,
        maxNodesPerRack, results, avoidStaleNodes, storageTypes);
    return writer;

官方说明:点击查看
摘:
For the common case, when the replication factor is three,HDFS’s placement policy is to put one replica on the local machine if the writer is on a datanode, otherwise on a random datanode, another replica on a node in a different (remote) rack, and the last on a different node in the same remote rack. This policy cuts the inter-rack write traffic which generally improves write performance. The chance of rack failure is far less than that of node failure;
译:
对于常见情况,当复制系数为3时,HDFS的放置策略是,如果写入程序位于datanode上,则将一个副本放置在本地计算机上,否则放置在随机datanode上,另一个副本放置在不同(远程)机架中的节点上,最后一个副本放置在同一远程机架中的不同节点上。此策略减少机架间写入通信量,这通常会提高写入性能。机架故障的概率远小于节点故障的概率;

3.3 NN和2NN的工作机制

NN 经常响应客户请求,所以元数据需存放在内存,提高效率,为了防止NN受损,丢失数据,此时需要有备份元数据的角色,即FsImage

NN 光有FsImage备份元数据,并不能满足同步更新,此时需要有进行元数据更新的角色,即 Edits,它负责存储追加或更新操作的元数据。

NN有了FsImageEdits以后还存在一个问题,Edits无法保存过多文件,同时二者又会定期合并,所以需要有负责合并的角色2NNSecondaryNameNode,专门用于FsImageEdits的合并。

3.4 NN与2NN的工作机制步骤图

在这里插入图片描述

3.4.1 第一阶段:NN启动

NN启动主要进行以下4个步骤:

  1. 第一次启动NN格式化后,创建 FsimageEdits 文件。若不是第一次启动就直接加载编辑日志和镜像文件到内存。
  2. Client对元数据进行增删改的请求。
  3. NN记录操作日志,更新滚动日志。
  4. NN 在内存中对元数据进行增删改。

3.4.2 第二阶段:2NN工作

2NN工作主要进行以下8个步骤:

  1. 2NN询问 NN 是否需要 CheckPoint,直接带回NN是否检查结果。
  2. 2NN 请求执行 CheckPoint
  3. NN 滚动正在写的 Edits 日志
  4. NN 将滚动前的编辑日志和镜像文件拷贝到2NN
  5. 2NN 加载编辑日志和镜像文件到内存,并合并
  6. 2NN生成新的镜像文件 fsimage.chkpoint
  7. 2NNfsimage.chkpoint拷贝到NN
  8. NNfsimage.chkpoint重新命名成fsimage

四、Fsimage和Edits的概念

参考Hadoop集群的配置文件hdfs-site.xml,比如笔者指定了 dfs.namenode.name.dir参数,即NN元数据存储位置,如下所示:

<property>
        <name>dfs.namenode.name.dir</name>
        <value>file:/opt/module/hadoop/namenode_dir</value>
</property>

官方默认的配置内容是:点击查看

<property>
  <name>dfs.namenode.name.dir</name>
  <value>file://${hadoop.tmp.dir}/dfs/name</value>
  <description>...</description>
</property>

以笔者的配置为例,NN格式化后,将在hadoop目录的 namenode_dir/current目录生成以下文件

fsimage_0000000000000000000
fsimage_0000000000000000000.md5
seen_txid
VERSION

关于这些文件的说明:

  • fsimage_xxxx文件:HDFS 文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目录和 inode的序列化信息
  • edits_xxxx文件:存放 HDFS 文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到 Edit文件中。(第一次进入后,未进行保存操作所以没有生成该文件)
  • seen_txid:保存的是一个数组,即最后一个edits_xxxx的数字
  • NN每次启动时,都会将Fsimage读入内存,加载 Edits里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成NN启动的时候就将 FsimageEdits文件进行了合并。

4.1 oiv命令查看 Fsimage文件

查看命令提示:

在启动HDFS后,输入hdfs命令会提示可操作的一些命令,现可以用grep过滤其中的提示信息,将oiv提取出来,或者直接hdfs oiv 查看

[root@hadoop101 current]# hdfs | grep oiv
oiv                  apply the offline fsimage viewer to an fsimage
oiv_legacy           apply the offline fsimage viewer to a legacy fsimage

在这里插入图片描述
基本语法:

hdfs oiv -p 文件类型 -i镜像文件 -o 转换后文件输出路径

案例: 将 fsimage镜像文件转化为 XML 文件,并cat查看
(笔者之前进行过HDFS的操作,所以有edits和fsimage文件)

hdfs oiv -p XML -i fsimage_0000000000000013555 -o ./fsimage_visted.xml
 
 cat ./fsimage_visted.xml

查看结果:
在这里插入图片描述
有时候Fsimage中没有记录块对应的DN

原因是:集群未启动,只有在集群启动后,NN要求DN上传Block信息,并且是间隔一段时间才会再次上报。

4.2 oev 命令查看 Edits 文件

基本语法:

hdfs oev -p 文件类型 -i 编辑日志 -o 转换后文件输出路径

案例: 将edits日志转化为XML并查看

hdfs oev -p XML -i edits_0000000000000000001-0000000000000000001 -o ./edits-visted.xml

cat ./edits-visted.xml

在这里插入图片描述

4.3 CheckPoint 时间设置

  • 通常情况下,2NN每隔一小时刷新一次

hdfs-default.xml点击查看

<property>
  <name>dfs.namenode.checkpoint.period</name>
  <value>3600s</value>
  <description>...</description>
</property>
  • 1分钟检查1次操作次数,当操作次数达到100万时,2NN执行1次
    hdfs-default.xml点击查看
<property>
  <name>dfs.namenode.checkpoint.txns</name>
  <value>1000000</value>
  <description>...操作动作次数</description>
</property>

<property>
  <name>dfs.namenode.checkpoint.check.period</name>
  <value>60s</value>
  <description>...1分钟检查1次的操作次数</description>
</property>

五、DN 工作机制

在这里插入图片描述

  1. 一个Block数据块在DN上以文件形式存储在磁盘,包括两个文件,一个是数据本身,另一个是元数据,包括数据块的长度、块数据的校验和以及时间戳。
  2. DN启动后向NN注册,通过后,DN周期性(6小时1次)得向NN上报所有的块信息,这时间来自配置文件:

hdfs-default.xml点击查看

<property>
  <name>dfs.blockreport.intervalMsec</name>
  <value>21600000</value>
  <description>...DN向NN汇报当前解读信息的时间间隔,默认6小时1次</description>
</property>

<property>
  <name>dfs.datanode.directoryscan.interval</name>
  <value>21600s</value>
  <description>...DN扫描自己节点块信息列表的时间,默认6小时1次</description>
</property>
  1. DN的心跳是每3秒一次,心跳返回结果带有 NN给该DN的命令,比如复制块数据到另一台机器,或者删除某个Block,如果超过10分钟NN没有收到某个DN的心跳,则认为该DN节点不可用。
  2. 集群运行过程中可以安全进入和退出一些机器。

5.1 DN保证数据完整性

DN保证数据完整性的方法:

  1. DN读取 Block时,会计算CheckSum
  2. 若计算后的CheckSumBlock 创建时值不一样,说明 Block已损坏。
  3. Client读取其他的 DN 上的 Block
  4. 常见校验算法,crc(32),MD5(128),sha1(160)
  5. DN 在其文件创建后会周期验证 CheckSum

5.2 DN掉线的时限制参数设置

  1. DN进程死亡或网络故障造成DN无法与NN通信。
  2. NN不会立即把该节点判定会死亡,要经过一段时间,这段时间暂称作超时时长。
  3. HDFS 默认的超时时长为 10 分钟 + 30秒。
  4. 若定义超时时间为 TimeOut,则它的计算公式为:

T i m e O u t = 2 ∗ r e c h e c k I n t e r v a l + 10 ∗ i n t e r v a l TimeOut = 2 * recheckInterval +10 * interval TimeOut=2recheckInterval+10interval

其中, r e q c h e c k I n t e r v a l reqcheckInterval reqcheckInterval 取值为 dfs.namenode.heartbeat.recheck-interval,默认为5分钟,

i n t e r v a l interval interval 取值为dfs.heartbeat.interval,默认为3秒,若需要自定义的话就修改hdfs-site.xml配置文件。

参考默认配置文件:[hdfs-default.xml] 点击查看

<property>
  <name>dfs.namenode.full.block.report.lease.length.ms</name>
  <value>300000</value>
  <description>..DN验证时间</description>
</property>

<property>
  <name>dfs.heartbeat.interval</name>
  <value>3s</value>
  <description>..DN心跳时间</description>
</property>
举报

相关推荐

0 条评论