一、概述

1. 产生背景

随着数据量越来越大,一台操作系统难以存储那么多的数据,那就用多台系统进行存储,但是这样管理维护起来很不方便,所以就需要一种系统来管理多个机器上的文件,这个系统就是分布式文件管理系统。HDFS只是其中的一种。

2. 定义

HDFS (Hadoop Distributed File System) 是一个分布式文件系统,通过目录树对文件进行定位,基于多台机器实现,每台机器各有各的角色。多适用于文件一次写入,多次读出。

3. 优缺点

  • 优点
    • 高容错 数据自动保存多个副本,当副本丢失后,会自动进行恢复
    • 适合大数据 能够处理 GB TB乃至 PB 级别的数据,能够处理百万级别的文件数目
    • 对机器性能要求不高 可以安装在一些廉价的机器上
  • 缺点

    • 数据访问延迟较大,不适合低延迟访问
    • 不适合对大量小文件进行存储。存储大量小文件 会过多的占用namenode内存来存储文件的目录和快信息,但是namenode是基于内存的,内存有限;此外大量小文件会增加寻址时间
    • 不支持对同一个文件的并发写入
    • 不知处对文件的随机修改。仅支持数据的添加 append

      4. 架构

      image.png
      image.png
  • NameNode (NN) 它是 master ,管理着集群下面的所有节点 ,

    • 管理HDFS 的命名空间
    • 配置 副本策略
    • 管理数据块的映射信息
    • 和客户端进行交互 处理客户端 读写请求
  • DataNode 是worker 负责执行 NN 的操作
    • 以快的形式存储实际的数据
    • 执行对数据的读写操作
  • Client
    • 负责文件的切分。上传文件时,将文件且分为一个个块,进行上传
    • 与 NN 进行交互 获取文件的位置信息
    • 与 DN 进行交互 完成对文件的操作 (读出/写入)
    • 提供一些命令 管理 HDFS ,比如 安装完成后对 NN 进行初始化,以及文件的增删改查操作
  • SecondryNameNode (2NN)
    • 它并非是NN 的热备,当NN 挂掉之后 ,并不能立马替换NN,提供服务
    • 辅助NameNode ,减少NN 的压力。比如 定期 合并 simage 和 edits ,并将结果推给NN
    • 在紧急情况下 可以辅助恢复 NN

      5. HDFS 块大小(取决于磁盘传输速率)

      hdfs 中的文件 是块为单位存储在磁盘上的,可以通过 dfs.blocksize 来指定,在2.x和3.x 中,块大小默认是128M ,而在1.x 版本中是 64M。如何根据实际的需要确定块大小呢?
      这里 我们假设 寻址耗时 10ms ,最佳情况下 寻址时间是传输时间的 1% ,那么对应的 传输时间是 10 / 1% = 1000ms = 1s 。这么来看 起决定因素的就是 磁盘的传输效率了。假设传输速率是 100M/S;那么 块大小是 100 * 1 = 100M 但是一般设计为2的N次方的形式。所以最后是128M
      如果 块大小设计的太小的话 会增加寻址时间;如果太大,磁盘是数据传输时间会明显大于定位时间,导致程序处理缓慢

      二、 shell 操作

      1. 基本语法

      hadoop fs 具体命令 或者说 hdfs dfs 具体命令

      2. 命令大全

      ```bash [allen@hadoop102 ~]$ hadoop fs Usage: hadoop fs [generic options] [-appendToFile ] [-cat [-ignoreCrc] …] [-checksum …] [-chgrp [-R] GROUP PATH…] [-chmod [-R] PATH…] [-chown [-R] [OWNER][:[GROUP]] PATH…] [-copyFromLocal [-f] [-p] [-l] [-d] [-t ] ] [-copyToLocal [-f] [-p] [-ignoreCrc] [-crc] ] [-count [-q] [-h] [-v] [-t []] [-u] [-x] [-e] …] [-cp [-f] [-p | -p[topax]] [-d] ] [-createSnapshot []] [-deleteSnapshot ] [-df [-h] [ …]] [-du [-s] [-h] [-v] [-x] …] [-expunge] [-find …] [-get [-f] [-p] [-ignoreCrc] [-crc] ] [-getfacl [-R] ] [-getfattr [-R] {-n name | -d} [-e en] ] [-getmerge [-nl] [-skip-empty-file] ] [-head ] [-help [cmd …]] [-ls [-C] [-d] [-h] [-q] [-R] [-t] [-S] [-r] [-u] [-e] [ …]] [-mkdir [-p] …] [-moveFromLocal ] [-moveToLocal ] [-mv ] [-put [-f] [-p] [-l] [-d] ] [-renameSnapshot ] [-rm [-f] [-r|-R] [-skipTrash] [-safely] …] [-rmdir [—ignore-fail-on-non-empty] …] [-setfacl [-R] [{-b|-k} {-m|-x } ]|[—set ]] [-setfattr {-n name [-v value] | -x name} ] [-setrep [-R] [-w] …] [-stat [format] …] [-tail [-f] [-s ] ] [-test -[defsz] ] [-text [-ignoreCrc] …] [-touch [-a] [-m] [-t TIMESTAMP ] [-c] …] [-touchz …] [-truncate [-w] …] [-usage [cmd …]]

Generic options supported are: -conf specify an application configuration file -D define a value for a given property -fs file:///|hdfs://namenode:port specify default filesystem URL to use, overrides ‘fs.defaultFS’ property from configurations. -jt specify a ResourceManager -files specify a comma-separated list of files to be copied to the map reduce cluster -libjars specify a comma-separated list of jar files to be included in the classpath -archives specify a comma-separated list of archives to be unarchived on the compute machines

The general command line syntax is: command [genericOptions] [commandOptions]

  1. <a name="gJ9kX"></a>
  2. ### 3. 常用命令
  3. 1. 启动hadoop 集群
  4. myhadoop start 或者 sbin/start-dfs.sh
  5. 2. 查看某个命令的帮助
  6. hadoop fs -help 具体命令
  7. 3. 上传文件
  8. 1. -moveFromLocal 文件名称 HDFS path : 从本地剪贴到HDFS
  9. vim shuguo.txt<br />shuguo<br />hadoop fs -moveFromLocal ./shuguo.txt /sanguo
  10. 2. -copyFromLocal 文件名 HDFS path : 从本地复制到 HDFS
  11. vim weiguo.txt<br />weiguo<br />hadoop fs -copyFromLocal weiguo.txt /sanguo
  12. 3. -put 文件名 HDFS path : 和 copyFromLocal 一样 常用这个
  13. vim wuguo.txt<br />wuguo<br />hadoop fs -put ./wuguo.txt /sanguo
  14. 4. -appendToFile 文件名 HDFS path+文件名 : 追加到指定文件后面
  15. vim liubei.txt<br />liubei<br />hadoop fs -appendToFile liubei.txt /sanguo/shuguo.txt
  16. 4. 下载文件
  17. 1. -copyToLocal HDFS path+文件名 文件路径 :将 HDFS 的文件 copy到本地
  18. hadoop fs -copyToLocal /sanguo/shuguo.txt ./
  19. 2. -get HDFS path+文件名 文件路径 : 和上条功能一样。常用这个
  20. hadoop fs -get /sanguo/shuguo.txt ./shuguo2.txt
  21. 5. -ls HDFS path : 显示目录信息
  22. hadoop fs -ls /sanguo
  23. 6. -cat HDFS path+文件名 : 显示文件内容
  24. hadoop fs -cat /sanguo/shuguo.txt
  25. 7. -chgrp/-chmod/-chown HDFS path+文件名 :Linux文件系统中的用法一样,修改文件所属权限
  26. hadoop fs -chmod 666 /sanguo/shuguo.txt
  27. 8. -mkdir HDFS newPath+文件名 : 创建新的目录
  28. hadoop fs -mkdir /jinguo
  29. 9. -cp HDFS oldPath+文件名 HDFS newPath : 将文件从 oldPath 拷贝到 newPath
  30. hadoop fs -cp /sanguo/shuguo.txt /jinguo
  31. 10. -mv HDFS oldPath+文件名 HDFS newPath : 将文件从 oldPath 移动到 newPath
  32. hadoop fs -mv /sanguo/wuguo.txt /jinguo
  33. 11. -tail HDFS path+文件名 : 显示文件末尾 1kb 的数据
  34. hadoop fs -tail /jinguo/shuguo.txt<br />
  35. 12. -rm (-r) HDFS path+文件名 : (递归)删除指定文件/文件夹
  36. hadoop fs -rm -r /sanguo
  37. 13. -du HDFS path : 统计文件夹大小信息
  38. adoop fs -du -s -h /jinguo<br />输出 : 27 81 /jinguo<br /> hadoop fs -du -h /jinguo<br />输出 :14 42 /jinguo/shuguo.txt<br />7 21 /jinguo/weiguo.txt<br />6 18 /jinguo/wuguo.tx
  39. 14. -setrep number HDFS path+文件名 :设置HDFS中文件的副本数量
  40. hadoop fs -setrep 10 /jinguo/shuguo.txt <br /> 注意 : 这里设置的副本数只是记录在NameNode的元数据中,是否真的会有这么多副本,还得看DataNode的数量。因为目前只有3台设备,最多也就3个副本,只有节点数的增加到10台时,副本数才能达到10。
  41. <a name="QSCpJ"></a>
  42. ## 三 API 操作
  43. <a name="VetC1"></a>
  44. ### 1. 准备
  45. - 下载 window 依赖文件
  46. - 配置 环境变量 HADOOP_HOME 目录
  47. - 验证 hadoop 环境变量 双击 winutils.exe,如果一闪而过 就没问题。
  48. - 创建maven 项目 导入下面的依赖
  49. ```xml
  50. <dependencies>
  51. <dependency>
  52. <groupId>org.apache.hadoop</groupId>
  53. <artifactId>hadoop-client</artifactId>
  54. <version>3.1.3</version>
  55. </dependency>
  56. <dependency>
  57. <groupId>junit</groupId>
  58. <artifactId>junit</artifactId>
  59. <version>4.12</version>
  60. </dependency>
  61. <dependency>
  62. <groupId>org.slf4j</groupId>
  63. <artifactId>slf4j-log4j12</artifactId>
  64. <version>1.7.30</version>
  65. </dependency>
  66. </dependencies>
  • 在 main/resources 目录下创建 文件 log4j.properties 并写入:

    1. log4j.rootLogger=INFO, stdout
    2. log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    3. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    4. log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
    5. log4j.appender.logfile=org.apache.log4j.FileAppender
    6. log4j.appender.logfile.File=target/spring.log
    7. log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
    8. log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
  • 创建测试类

    public class HdfsClient {
    
    @Test
    public void testMkdirs() throws IOException, URISyntaxException, InterruptedException {
    
      // 1 获取文件系统
      Configuration configuration = new Configuration();
    
      // FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration);
      FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration,"allen");
    
      // 2 创建目录
      fs.mkdirs(new Path("/xiyou/huaguoshan/"));
    
      // 3 关闭资源
      fs.close();
    }
    }
    

    2. 上传文件

    @Test
    public void testCopyFromLocalFile() throws IOException, InterruptedException, URISyntaxException {
    
      // 1 获取文件系统
      Configuration configuration = new Configuration();
      configuration.set("dfs.replication", "2");
      FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "allen");
    
      // 2 上传文件
      fs.copyFromLocalFile(new Path("d:/sunwukong.txt"), new Path("/xiyou/huaguoshan"));
    
      // 3 关闭资源
      fs.close();
    }
    

    关于参数优先级: (1)客户端代码中设置的值 >(2)ClassPath下的用户自定义配置文件>(3)然后是服务器的自定义配置(xxx-site.xml) >(4)服务器的默认配置(xxx-default.xml)

    3.文件下载

    @Test
    public void testCopyToLocalFile() throws IOException, InterruptedException, URISyntaxException{
    
      // 1 获取文件系统
      Configuration configuration = new Configuration();
      FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "allen");
    
      // 2 执行下载操作
      // boolean delSrc 指是否将原文件删除
      // Path src 指要下载的文件路径
      // Path dst 指将文件下载到的路径
      // boolean useRawLocalFileSystem 是否开启文件校验
      fs.copyToLocalFile(false, new Path("/xiyou/huaguoshan/sunwukong.txt"), new Path("d:/sunwukong2.txt"), true);
    
      // 3 关闭资源
      fs.close();
    }
    

    4. 文件重命名和移动

    @Test
    public void testRename() throws IOException, InterruptedException, URISyntaxException{
    
      // 1 获取文件系统
      Configuration configuration = new Configuration();
      FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "allen"); 
    
      // 2 修改文件名称
      fs.rename(new Path("/xiyou/huaguoshan/sunwukong.txt"), new Path("/xiyou/huaguoshan/meihouwang.txt"));
    
      // 3 关闭资源
      fs.close();
    }
    

    5. 删除文件和目录

    @Test
    public void testDelete() throws IOException, InterruptedException, URISyntaxException{
    
      // 1 获取文件系统
      Configuration configuration = new Configuration();
      FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "allen");
    
      // 2 执行删除
      fs.delete(new Path("/xiyou"), true);
    
      // 3 关闭资源
      fs.close();
    }
    

    6. 查看文件详情

    @Test
    public void testListFiles() throws IOException, InterruptedException, URISyntaxException {
    
      // 1获取文件系统
      Configuration configuration = new Configuration();
      FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "allen");
    
      // 2 获取文件详情
      RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);
    
      while (listFiles.hasNext()) {
          LocatedFileStatus fileStatus = listFiles.next();
    
          System.out.println("========" + fileStatus.getPath() + "=========");
          System.out.println(fileStatus.getPermission());
          System.out.println(fileStatus.getOwner());
          System.out.println(fileStatus.getGroup());
          System.out.println(fileStatus.getLen());
          System.out.println(fileStatus.getModificationTime());
          System.out.println(fileStatus.getReplication());
          System.out.println(fileStatus.getBlockSize());
          System.out.println(fileStatus.getPath().getName());
    
          // 获取块信息
          BlockLocation[] blockLocations = fileStatus.getBlockLocations();
          System.out.println(Arrays.toString(blockLocations));
      }
      // 3 关闭资源
      fs.close();
    }
    

    7. 文件/文件夹 判断

    ```java @Test public void testListStatus() throws IOException, InterruptedException, URISyntaxException{

    // 1 获取文件配置信息 Configuration configuration = new Configuration(); FileSystem fs = FileSystem.get(new URI(“hdfs://hadoop102:8020”), configuration, “allen”);

    // 2 判断是文件还是文件夹 FileStatus[] listStatus = fs.listStatus(new Path(“/“));

    for (FileStatus fileStatus : listStatus) {

      // 如果是文件
      if (fileStatus.isFile()) {
          System.out.println("f:"+fileStatus.getPath().getName());
      }else {
          System.out.println("d:"+fileStatus.getPath().getName());
      }
    

    }

    // 3 关闭资源 fs.close(); }

<a name="ViYLp"></a>
## 四 HDFS 读写流程
<a name="kUHMb"></a>
### 1.文件写流程
![image.png](https://cdn.nlark.com/yuque/0/2022/png/23124036/1647356113994-0512e16b-05d7-48dc-873e-fc52b92ec108.png#clientId=u463093db-97dc-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=736&id=u372e6171&margin=%5Bobject%20Object%5D&name=image.png&originHeight=920&originWidth=1893&originalType=binary&ratio=1&rotation=0&showTitle=false&size=206657&status=done&style=none&taskId=u7ace8300-2321-4362-8902-d964e63fb7d&title=&width=1514.4)

1. 客户端通过 Distributed FileSystem 模块向 NameNode 发送上传文件请求
1. NameNode 收到后检查文件目录是否存在、是否有资格上传文件以及文件是否已存在,均满足后,向client 回应可以上传数据
1. 客户端检查文件大小,根据设置的块大小进行数据切分,切分后向 NameNode 发送上传请求
1. NameNode  收到后 查询 DataNode 状态 返回可以上传数据的节点 
1. 客户端收后通过 FSDataOutputStream 模块向其中一个节点发送文件上传请求,
1.  DataNode1 收到后请求调用DataNode2,依次调用,通信通道建立后返回给 client
1. 客户端开始传输第一个块,以 packet 为单位上传文件到 DataNode1 ,DataNode1 收到后发送给 DataNode2 依次进行,DataNode1 每传递一个 packet 就会放入应答队列等来回应
1. 第一个文件块传输完成后,重复进行 3-7 步
<a name="iTjc0"></a>
### 2. 机架感知算法
<a name="URKcY"></a>
#### 1. 节点的计算
在HDFS写数据的过程中,NameNode 会选择距离最近的 DataNode接受数据。最近距离说的是节点距离,即两个节点到达最近相同祖先的距离之和最小。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/23124036/1647357168558-4dced45c-4bb7-4782-8be2-f363d85ecdb4.png#clientId=u463093db-97dc-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=734&id=ud00c7956&margin=%5Bobject%20Object%5D&name=image.png&originHeight=917&originWidth=1888&originalType=binary&ratio=1&rotation=0&showTitle=false&size=65199&status=done&style=none&taskId=u07ad8dff-9f3f-48c9-a5c4-19238fecc5e&title=&width=1510.4)
<a name="woZw0"></a>
#### 2. 机架感知算法(副本存储节点选择)

- 官方说明 

[http://hadoop.apache.org/docs/r3.1.3/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html#Data_Replication](http://hadoop.apache.org/docs/r3.1.3/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html#Data_Replication)

- 副本节点的选择    

![image.png](https://cdn.nlark.com/yuque/0/2022/png/23124036/1647357856396-59270da7-7dbd-49d1-9093-dbf0f73a50dc.png#clientId=u463093db-97dc-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=403&id=u949640fa&margin=%5Bobject%20Object%5D&name=image.png&originHeight=806&originWidth=1058&originalType=binary&ratio=1&rotation=0&showTitle=false&size=57047&status=done&style=none&taskId=ubd4f3e76-979f-49cc-8509-f8904826a69&title=&width=529)

   - 第一个副本在 client 节点上,如果client 在集群外  随机选择一个
   - 第二个副本在另一个机架上随机的有个节点
   - 第三个副本在第二个副本所在节点的随机节点
<a name="ffNEX"></a>
#### 3. 读数据流程
![image.png](https://cdn.nlark.com/yuque/0/2022/png/23124036/1647395473329-67854e4a-84eb-4dd5-9ffb-e1a2c8e945e1.png#clientId=u5d894947-6598-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=726&id=u03eb78f2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=908&originWidth=1892&originalType=binary&ratio=1&rotation=0&showTitle=false&size=115695&status=done&style=none&taskId=u070dc008-93ad-4fe5-8675-e004dbb9896&title=&width=1513.6)

1. 客户端通过 DistributedFileSystem 向 NameNode 发送请求下载文件请求。NameNode 收到后 查询元数据,找到后 返回文件块所在的 DataNode 地址
1. 客户端收到后,从返回的 DataNode 中挑选一台 NameNode (先就近原则然后随机)服务器,请求读取数据
1. DataNode 开始传输数据给客户端(从磁盘里面读取数据输出流 以packet 为单位做校验)
1. 客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。
<a name="WEwz1"></a>
## 五、 NameNode 和 SecondaryNameNode
<a name="l9roy"></a>
### 1.NN 和 2NN 工作机制
我们知道 NameNode 节点需要经常和客户端交互,进行随机访问并且还要相应客户请求,如果  NameNode 将数据存放在磁盘中,那必然效率降低。所以 NameNode 的数据应该存放在内存中。但是如果只存在于内存中,数据一旦断电,元数据丢失,整个集群就无法工作。因此产生了备份元数据的 FsImage ,存放在磁盘中。<br />引入 FsImage 后,又产生了新的问题,当内存中的数据更新时,必然要更新 FsImage  这样导致效率过低,如果不更新的话,就难以保证一致性的问题。这时 引入 Edits 文件 ,只进行追加操作,效率很高。每当元数据更新时,修改内存中的元数据并追加到 Edits 文件中。这样。一旦 NameNode 断电,可以通过合并 FsImage 和 Edits 文件 得到元数据。<br />但是,如果长时间一直往 Edits  进行追加,必然导致文件数据过大,而且一旦断电,回复时间也很长。因此需要定期合并 FsImage 和 Edits 文件。如果这个操作交给 NameNode 来完成。其效率又会降低,因此引入一个新的节点 SecondaryNameNode 专门用于这两个文件的合并<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/23124036/1647412220222-af0891e3-30ab-4ce2-8b3d-e0daba88607e.png#clientId=u5d894947-6598-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=788&id=u71cc8651&margin=%5Bobject%20Object%5D&name=image.png&originHeight=985&originWidth=1888&originalType=binary&ratio=1&rotation=0&showTitle=false&size=217424&status=done&style=none&taskId=u04def3df-26f3-42ec-975a-3496422d235&title=&width=1510.4)

- 第一阶段 NameNode 启动
   - 如果是第一次启动 NameNode  格式化后,会创建 Fsimage 和 Edits 文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存
   - 客户端 对元数据进行增删改请求
   - NameNode 记录操作日志,更新滚动日志
   - NameNode 在内存中对元数据进行增删改
- 第二阶段 SecondaryNameNode 工作
   - SecondaryNameNode 每隔一定的时间就会询问 NameNode 是否需要 CheckPoint,返回是否检查
   - 如果是  SecondaryNameNode  请求执行 CheckPoint
   - NameNode  滚动正在写的 Edits 日志,将滚共之前的  Fsimage 和 Edits 文件 到SecondaryNameNode 
   - SecondaryNameNode  收到后 加载到内存中,然后合并,生成新的镜像文件 fsimage.chkpoint,最后拷贝到 NameNode
   - NameNode 将 fsimage.chkpoint 重命名为  fsimage
<a name="O6uKk"></a>
### 2. Fsimage 和 Edits
进入hadoop安装目录下的 data/dfs/name/current <br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/23124036/1647416580691-348a7e9e-aac9-4c64-b8d0-26f5b0e3f81e.png#clientId=u5d894947-6598-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=237&id=u1de2377e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=296&originWidth=918&originalType=binary&ratio=1&rotation=0&showTitle=false&size=34231&status=done&style=none&taskId=u1b8dfe6b-7d70-4b4d-a8fb-10b509ee5c0&title=&width=734.4)

- fsimage : 存放的是 HDFS 文件系统元数据的一个永久性检查点。包含 HDFS 文件系统的所有目录和文件inode 的序列化信息
- edits  : 记录的是对 HDFS 文件系统的所有更新操作。文件系统的所有操作都是先记录后执行的
- seen_txid : 保存的是一个整数,就是 edits 的最后一个文件
- 每次 NameNode 启动的时候 都会将 fsimage 文件读入到内存中,然后加载 edits 操作,进而保证内存中的元数据是最新的。

- 使用 ovi 和 oev 查看 日志文件
   - 查看 oiv  和 oev 命令

![image.png](https://cdn.nlark.com/yuque/0/2022/png/23124036/1647424118797-6674939d-ad16-4553-8e50-d31750504cab.png#clientId=u01a5fe11-4a91-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=450&id=u7d25604a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=562&originWidth=730&originalType=binary&ratio=1&rotation=0&showTitle=false&size=38162&status=done&style=none&taskId=u3062a0fd-b39b-41ec-9d57-9b01c561258&title=&width=584)

   - 基本语法

hdfs oiv -p 文件类型 -i镜像文件 -o 转换后文件输出路径<br />hdfs oev -p 文件类型 -i编辑日志 -o 转换后文件输出路径
```xml
hdfs oiv -p XML -i fsimage_0000000000000000025 -o /opt/module/hadoop-3.1.3/fsimage.xml

<INodeSection><lastInodeId>16427</lastInodeId><numInodes>29</numInodes><inode><id>16385</id><type>DIRECTORY</type><name></name><mtime>1647352508635</mtime><permission>allen:supergroup:0755</permission><nsquota>9223372036854775807</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16386</id><type>DIRECTORY</type><name>tmp</name><mtime>1647185206195</mtime><permission>allen:supergroup:0770</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16387</id><type>DIRECTORY</type><name>hadoop-yarn</name><mtime>1647183721249</mtime><permission>allen:supergroup:0770</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16388</id><type>DIRECTORY</type><name>staging</name><mtime>1647185202938</mtime><permission>allen:supergroup:0770</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16389</id><type>DIRECTORY</type><name>history</name><mtime>1647183721284</mtime><permission>allen:supergroup:0770</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16390</id><type>DIRECTORY</type><name>done</name><mtime>1647185325080</mtime><permission>allen:supergroup:0770</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16391</id><type>DIRECTORY</type><name>done_intermediate</name><mtime>1647185208510</mtime><permission>allen:supergroup:1777</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16392</id><type>DIRECTORY</type><name>input</name><mtime>1647184908591</mtime><permission>allen:supergroup:0755</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16393</id><type>FILE</type><name>word.txt</name><replication>3</replication><mtime>1647184908586</mtime><atime>1647184907628</atime><preferredBlockSize>134217728</preferredBlockSize><permission>allen:supergroup:0644</permission><blocks><block><id>1073741825</id><genstamp>1001</genstamp><numBytes>25</numBytes></block>
    </blocks>
    <storagePolicyId>0</storagePolicyId></inode>
  <inode><id>16394</id><type>DIRECTORY</type><name>allen</name><mtime>1647185202938</mtime><permission>allen:supergroup:0700</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16395</id><type>DIRECTORY</type><name>.staging</name><mtime>1647185222419</mtime><permission>allen:supergroup:0700</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16401</id><type>DIRECTORY</type><name>logs</name><mtime>1647185206225</mtime><permission>allen:allen:1777</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16402</id><type>DIRECTORY</type><name>allen</name><mtime>1647185206228</mtime><permission>allen:allen:0770</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16403</id><type>DIRECTORY</type><name>logs-tfile</name><mtime>1647185206230</mtime><permission>allen:allen:0770</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16404</id><type>DIRECTORY</type><name>application_1647184622023_0001</name><mtime>1647185228920</mtime><permission>allen:allen:0770</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16405</id><type>DIRECTORY</type><name>allen</name><mtime>1647185325094</mtime><permission>allen:supergroup:0770</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16406</id><type>DIRECTORY</type><name>output</name><mtime>1647185221242</mtime><permission>allen:supergroup:0755</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16413</id><type>FILE</type><name>part-r-00000</name><replication>3</replication><mtime>1647185221161</mtime><atime>1647185221059</atime><preferredBlockSize>134217728</preferredBlockSize><permission>allen:supergroup:0644</permission><blocks><block><id>1073741832</id><genstamp>1008</genstamp><numBytes>25</numBytes></block>
    </blocks>
    <storagePolicyId>0</storagePolicyId></inode>
  <inode><id>16415</id><type>FILE</type><name>_SUCCESS</name><replication>3</replication><mtime>1647185221245</mtime><atime>1647185221242</atime><preferredBlockSize>134217728</preferredBlockSize><permission>allen:supergroup:0644</permission><storagePolicyId>0</storagePolicyId></inode>
  <inode><id>16418</id><type>FILE</type><name>job_1647184622023_0001-1647185204027-allen-word+count-1647185221035-1-1-SUCCEEDED-default-1647185211185.jhist</name><replication>3</replication><mtime>1647185221339</mtime><atime>1647185221312</atime><preferredBlockSize>134217728</preferredBlockSize><permission>allen:supergroup:0770</permission><blocks><block><id>1073741834</id><genstamp>1010</genstamp><numBytes>22303</numBytes></block>
    </blocks>
    <storagePolicyId>0</storagePolicyId></inode>
  <inode><id>16419</id><type>FILE</type><name>job_1647184622023_0001_conf.xml</name><replication>3</replication><mtime>1647185221370</mtime><atime>1647185221346</atime><preferredBlockSize>134217728</preferredBlockSize><permission>allen:supergroup:0770</permission><blocks><block><id>1073741835</id><genstamp>1011</genstamp><numBytes>214642</numBytes></block>
    </blocks>
    <storagePolicyId>0</storagePolicyId></inode>
  <inode><id>16420</id><type>FILE</type><name>hadoop104_35611</name><replication>3</replication><mtime>1647185228546</mtime><atime>1647185228466</atime><preferredBlockSize>134217728</preferredBlockSize><permission>allen:allen:0640</permission><blocks><block><id>1073741836</id><genstamp>1012</genstamp><numBytes>66104</numBytes></block>
    </blocks>
    <storagePolicyId>0</storagePolicyId></inode>
  <inode><id>16421</id><type>FILE</type><name>hadoop103_43961</name><replication>3</replication><mtime>1647185228915</mtime><atime>1647185228831</atime><preferredBlockSize>134217728</preferredBlockSize><permission>allen:allen:0640</permission><blocks><block><id>1073741837</id><genstamp>1013</genstamp><numBytes>67663</numBytes></block>
    </blocks>
    <storagePolicyId>0</storagePolicyId></inode>
  <inode><id>16422</id><type>DIRECTORY</type><name>2022</name><mtime>1647185325080</mtime><permission>allen:supergroup:0770</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16423</id><type>DIRECTORY</type><name>03</name><mtime>1647185325080</mtime><permission>allen:supergroup:0770</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16424</id><type>DIRECTORY</type><name>13</name><mtime>1647185325080</mtime><permission>allen:supergroup:0770</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16425</id><type>DIRECTORY</type><name>000000</name><mtime>1647185325094</mtime><permission>allen:supergroup:0770</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16426</id><type>DIRECTORY</type><name>xiyou</name><mtime>1647352508636</mtime><permission>allen:supergroup:0755</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
  <inode><id>16427</id><type>DIRECTORY</type><name>huaguoshan</name><mtime>1647352508636</mtime><permission>allen:supergroup:0755</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
    </INodeSection>
hdfs oev -p XML -i edits_0000000000000000012-0000000000000000013 -o /opt/module/hadoop-3.1.3/edits.xml


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<EDITS>
  <EDITS_VERSION>-64</EDITS_VERSION>
  <RECORD>
    <OPCODE>OP_START_LOG_SEGMENT</OPCODE>
    <DATA>
      <TXID>145</TXID>
    </DATA>
  </RECORD>
</EDITS>

3. Check Point 时间设置

  • 通常情况下 SecondaryNameNode 每隔一小时执行一次

    <property>
    <name>dfs.namenode.checkpoint.period</name>
    <value>3600s</value>
    </property>
    
  • 一分钟检查一次操作数 当操作数达到1百万时 SecondaryNameNode 执行一次 ```xml

    dfs.namenode.checkpoint.txns 1000000 操作动作次数

dfs.namenode.checkpoint.check.period 60s

1分钟检查一次操作次数

<a name="uzKkw"></a>
## 六 DataNode 
<a name="LMy5X"></a>
### 1. 工作机制
![image.png](https://cdn.nlark.com/yuque/0/2022/png/23124036/1647435256671-c2d50cc2-7714-450a-a99a-c671023fb9b3.png#clientId=ud08d55a2-d408-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=739&id=u81e441fb&margin=%5Bobject%20Object%5D&name=image.png&originHeight=924&originWidth=1850&originalType=binary&ratio=1&rotation=0&showTitle=false&size=191898&status=done&style=none&taskId=ubafc7533-497d-4a09-9a5e-e63e886716e&title=&width=1480)

- 数据块 在DataNode 上以文件的形式存储在磁盘上,包括两个文件,一个是数据本身,另一个是元数据,包含数据块的长度、校验和、时间戳。
- DataNode 启动时 主动向 NameNode 注册,注册成功后 每 6h(默认) 向 NameNode 发送次块信息
- 每3s 向 NameNode 发送一次心跳,返回 NameNode 下达的命令,如 复制数据块 或者删除数据库。如果NameNode 10分钟还没收到来自 DataNode 的心跳,则认为该节点不可用
<a name="Rhgi4"></a>
### 2. 如何保障数据的完整性

- 当 DataNode 读取块信息时 会计算其数据校验和,如果和元数据中的 校验和不一致则说明块已被损坏
- 另外文件创建后,会周期性(6h)计算一次校验和。
- 常见的校验方法 : crc(32), md5(128),shal(128)
<a name="SeJs6"></a>
### 3. 掉线参数设置

1. 当 某个 DataNode 进程死亡或者因为网络故障无法与NameNode进行通信是,NameNode 不会立刻把该节点判定为死亡,要经过一段时间,这段时间叫做超时时长,默认是10分钟+30s ,计算公式是:

TimeOut = 2 *dfs.namenode.heartbeat.recheck-interval+10* dfs.heartbeat.interval<br />需要注意的是hdfs-site.xml 配置文件中的heartbeat.recheck.interval的单位为毫秒,dfs.heartbeat.interval的单位为秒。
<a name="yOBrT"></a>
## 七、其他
<a name="o1H3g"></a>
### 1. 添加新节点

   1. 在新机器上配配置基本的环境
   1. 随意选择一台正在服役的节点 将其hadoop 安装目录拷贝到新机器上
   1. 删除目录中的log  和data  文件夹
   1. 启动 DataNode 和 NameNode 

 ` sbin/hadoop-daemon.sh start datanode `<br />` sbin/yarn-daemon.sh start nodemanager`

   5. 如果数据不平衡 ,平衡数据

 ` sbin/start-balancer.sh `
<a name="uWmxi"></a>
### 2. 退役旧的节点

- 添加白名单

创建 dfs.hosts 文件,凡是文件上有的主机节点都可以访问 NameNode ,不在名单上的节点,会被退役

   1. 在在NameNode的./etc/hadoop目录下创建dfs.hosts文件
   1. 添加白名单数据名称
```scala
hadoop102
hadoop103
hadoop104
  1. 在NameNode的hdfs-site.xml配置文件中增加dfs.hosts属性

    <property>
    <name>dfs.hosts</name>
    <value>/opt/module/hadoop-3.1.3/etc/hadoop/dfs.hosts</value>
    </property>
    
  2. 刷新NameNode

    hdfs dfsadmin -refreshNodes

  3. 更新 ResourceManager

yarn rmadmin -refreshNodes

  1. 如果数据不平衡 ,使用命令平衡数据

./start-balancer.sh

  • 黑名单退役

在黑名单上的机器都会被退役

  1. 在NameNode的/opt/module/hadoop-2.7.2/etc/hadoop目录下创建dfs.hosts.exclude文件
  2. 添加 需要退役的主机名称
  3. 在NameNode的hdfs-site.xml配置文件中增加dfs.hosts.exclude属性
  4. 刷新NameNode、刷新ResourceManager ```scala [allen@hadoop102 hadoop-3.1.3]$ hdfs dfsadmin -refreshNodes Refresh nodes successful

[allen@hadoop102 hadoop-3.1.3]$ yarn rmadmin -refreshNodes 22/03/24 14:55:56 INFO client.RMProxy: Connecting to ResourceManager at hadoop103/192.168.1.103:8033

```

  1. 检查web 浏览器

image.png

  1. 等待退役节点状态为decommissioned(所有块已经复制完成),停止该节点及节点资源管理器。注意:如果副本数是3,服役的节点小于等于3,是不能退役成功的,需要修改副本数后才能退役,
  2. 如果数据不均衡,可以用命令实现集群的再平衡

注意:不允许白名单和黑名单中同时出现同一个主机名称。

3. 小文件问题

  • 小文件弊端

每个文件都是按照块进行存储的,块的元信息存储在 NameNode 的内存中,因此HDFS 存储小文件效率特别低,因为大量小文件会耗尽 NameNode 的大部分内存。
IO 问题以及磁盘性能问题。

  • 解决方案:

使用 HDFS 存档文件(HAR) 文件,它是一个更高效的文件存档工具,将文件存入到 HDFS 中,减少 NameNode内存使用,允许对文件进行透明操作。具体就是:HDFS 存档文件对内是一个个独立的小文件,对NameNode 而言却是一个整体,减少了NameNode 的内存使用量。
image.png
具体操作 :

  1. 把/user/atguigu/input目录里面的所有文件归档成一个叫input.har的归档文件,并把归档后文件存储到/user/atguigu/output路径下。

bin/hadoop archive -archiveName input.har –p /user/atguigu/input /user/atguigu/output

  1. 查看归档

hadoop fs -lsr /user/atguigu/output/input.har

  1. 解归档文件

hadoop fs -cp har:/// user/atguigu/output/input.har/* /user/atguigu

4. HDFS 的 HA

1. HA 概述

  • 所谓的 HA 就是高可用 提供 7*24小时不断服务
  • 实现HA 高可用的关键是解决单点故障,主要是 HDFS 的 HA 和 YARN 的 HA

    2. HDFS HA 机制

    通过双 NameNode 消除单点故障

    3. 工作要点

  • 元数据管理方式需要改变

Edits 日志只有 Arctive 状态下的 NameNode 可以进行写操作,两个 NameNode 都可以进行读取Edits 。将共享的 Edits 放在一个 共享存储中管理。

  • 需要一个状态管理功能模块

实现一个 zkfailover ,常驻在每一个NameNode 所在的节点中,zkfailover负责监控自己所在的节点,利用ZK 进行标记,当需要状态切换的时候,有zkfailover 来负责切换,切换时放在脑裂现象

  • 必须保障两个 NameNode 之间能够 SSH 无密码登录
  • 隔离,两个 NameNode只能同时有一个对外提供服务

    5. HDFS 自动故障转移机制

    实现 HDFS 的自动故障转移需要部署两个新的组件,Zookeeper 和 ZKFailoverController(ZKFC),Zookeeper 负责维护少量的协调数据,通知客户端这些数据的改变和监视客户端的高可用服务,比如

  • 故障检测: 集群中每个 NameNode 在Zookeeper 中维护了一个持久会话,如果机器崩溃,与Zookeeper 的会话也会终止,Zookeeper 就会通知另一个 NameNode 触发故障转移。

  • 现役NameNode 的选择:Zookeeper 提供了一个简单的机制用于唯一的选择一个节点为 active 状态。如果当前现役的节点崩溃,另一个节点可能从 Zookeeper 中获得特殊的排外锁,表明它就是现役的NameNode

ZKFC 是Zookeeper 的客户端,也是监视和管理 NameNode 的状态,每个运行的NameNode 主机也会运行一个 ZKFC 进程,职责包括:

  • 健康监测:ZKFC 周期性的使用 一个健康检查命令 ping 与之存在相同主机的NameNode节点,只要该NameNode 及时的回复健康状态,ZKFC 就会认为该节点时健康的,如果没有及时回复,健康检测器就会认为该节点时非健康的
  • Zookeeper会话管理器:当本地的NameNode 是健康的,ZKFC 保持一个在Zookeeper 中打开的会话,如果本地的NameNode处于active 状态,,ZKFC也保持一个特殊的znode锁,该锁使用了ZooKeeper对短暂节点的支持,如果会话终止,锁节点将自动删除。
  • 基于ZooKeeper的选择如果本地NameNode是健康的,且ZKFC发现没有其它的节点当前持有znode锁,它将为自己获取该锁。如果成功,则它已经赢得了选择,并负责运行故障转移进程以使它的本地NameNode为Active。故障转移进程与前面描述的手动故障转移相似,首先如果必要保护之前的现役NameNode,然后本地NameNode转换为Active状态。

image.png

6. Yarn - HA 工作机制

image.png

7. 集群安全模式

1. 概述
  • NameNode 启动

当NameNode启动的时候,首先将镜像文件(Fsimage)载入到内存中,然后执行编辑日志(Edits)中的各项操作,一旦在内存中成功建立系统元数据映像,则会创建一个新的Fsimage文件和一个空的Edits 文件,此时开始监听NameNode的请求。这监听期间,NameNode 一直运行在安全模式,及NameNode 的文件系统对于用户来说只是只读的

  • DataNode 启动

系统的数据是以块的形式存储在NameNode中,在系统的正常操作期间,NameNode 会保存所有块位置的映射信息。在安全模式下,各个 DataNode 会向 NameNode 发送最新请求的块信息,NameNode 在了解到足够多的块信息后,退出安全模式,即可高效运行文件系统

2. 安全模式退出的判断

如果满足最小副本条件,NameNode 会在30秒后退出安全模式,所谓的最小副本条件说的是,整个文件系统中99.9% 的块满足最小副本级别(默认是1)。如果启动的是刚初始化的HDFS 集群时,因为系统中还没有任何块,NameNode不会进入到安全模式

  • 基本语法
    • 查看安全模式状态

bin/hdfs dfsadmin -safemode get

  • 进入安装模式状态

bin/hdfs dfsadmin -safemode enter

  • 离开安全模式状态

bin/hdfs dfsadmin -safemode leave

  • 等待安全模式状态

bin/hdfs dfsadmin -safemode wait