# HDFS分布式文件系统


<!--more-->


## 1. 简介

HDFS（全称：HadoopDistributeFileSystem，Hadoop分布式文件系统）是Hadoop核心组成，是分布式存储服务。

分布式文件系统横跨多台计算机，在大数据时代有着广泛的应用前景，它们为存储和处理超大规模数据提供所需的扩展能力。

## 2. 重要概念

HDFS通过统一的命名空间目录树来定位文件；另外，它是分布式的，由很多服务器联合起来实现其功能，集群中的服务器有各自的角色（分布式本质是拆分，各司其职）；

- 典型的Master/Slave架构

  HDFS的架构是典型的Master/Slave结构。

  HDFS集群往往是一个NameNode（HA架构会有两个NameNode,联邦机制）+多个DataNode组成；

  NameNode是集群的主节点，DataNode是集群的从节点。

- 分块存储（block机制）

  HDFS中的文件在物理上是分块存储（block）的，块的大小可以通过配置参数来规定；

  Hadoop2.x版本中默认的block大小是128M；

- 命名空间（NameSpace）

  HDFS支持传统的层次型文件组织结构。用户或者应用程序可以创建目录，然后将文件保存在这些目录里。文件系统名字空间的层次结构和大多数现有的文件系统类似：用户可以创建、删除、移动或重命名文件。

  Namenode负责维护文件系统的名字空间，任何对文件系统名字空间或属性的修改都将被Namenode记录下来。

  HDFS提供给客户单一个抽象目录树，访问形式：hdfs://namenode的hostname:port/test/input hdfs://linux121:9000/test/input 

- NameNode元数据管理

  我们把目录结构及文件分块位置信息叫做元数据。

  NameNode的元数据记录每一个文件所对应的block信息（block的id,以及所在的DataNode节点的信息）

- DataNode数据存储

  文件的各个block的具体存储管理由DataNode节点承担。一个block会有多个DataNode来存储，DataNode会定时向NameNode来汇报自己持有的block信息。

- 副本机制

  为了容错，文件的所有block都会有副本。每个文件的block大小和副本系数都是可配置的。应用程序可以指定某个文件的副本数目。

  副本系数可以在文件创建的时候指定，也可以在之后改变。

  副本数量默认是3个。

- 一次写入，多次读出

  HDFS是设计成适应一次写入，多次读出的场景，且不支持文件的随机修改。（支持追加写入，不只支持随机更新）正因为如此，HDFS适合用来做大数据分析的底层存储服务，并不适合用来做网盘等应用（修改不方便，延迟大，网络开销大，成本太高）

## 3. HDFS架构

![img_8](https://img.zhaojq.top/20260727091845368.png "img_8")

- NameNode(nn):Hdfs集群的管理者，Master

  维护管理Hdfs的名称空间（NameSpace）维护副本策略

  记录文件块（Block）的映射信息

  负责处理客户端读写请求

- DataNode:NameNode下达命令，DataNode执行实际操作，Slave节点。

  保存实际的数据块

  负责数据块的读写

- Client:客户端

  上传文件到HDFS的时候，Client负责将文件切分成Block,然后进行上传

  请求NameNode交互，获取文件的位置信息

  读取或写入文件，与DataNode交互

  Client可以使用一些命令来管理HDFS或者访问HDFS

## 4. 客户端操作

### 4.1 shell命令操作hdfs

`hadoop fs 具体命令 `

OR 

`hdfs dfs 具体命令 `

```
 bin/hdfs dfs
 
 # 可查看全部命令
```

- 显示目录信息 `hadoop fs -ls /`

- 在HDFS上创建目录 ` hadoop fs -mkdir -p /test/bigdata`

- 从本地剪切粘贴到HDFS `hadoop fs -moveFromLocal ./hadoop.txt /test/bigdata`

- 追加一个文件到已经存在的文件末尾 ` hadoop fs -appendToFile hdfs.txt  /test/bigdata/hadoop.txt`

- 显示文件内容 `hadoop fs -cat /test/bigdata/hadoop.txt`

-  -chgrp 、-chmod、-chown：Linux文件系统中的用法一样，修改文件所属权限

  ```
   hadoop fs -chmod 666 /test/bigdata/hadoop.txt
   hadoop fs -chown root:root
  ```

-  -copyFromLocal：从本地文件系统中拷贝文件到HDFS路径去`hadoop fs -copyFromLocal README.txt / `

-  -copyToLocal：从HDFS拷贝到本地`hadoop fs -copyToLocal /test/bigdata/hadoop.txt ./`

-  -cp ：从HDFS的一个路径拷贝到HDFS的另一个路径` hadoop fs -cp /test/bigdata/hadoop.txt /hdfs.txt`

-  -mv：在HDFS目录中移动文件 ` hadoop fs -mv /hdfs.txt /test/bigdata/`

-  -get：等同于copyToLocal，就是从HDFS下载文件到本地` hadoop fs -get /test/bigdata/hadoop.txt ./ `

-  -put：等同于copyFromLocal 

-  -tail：显示一个文件的末尾 `hadoop fs -tail /user/root/test/yarn.txt `

-  -rm：删除文件或文件夹 ` hadoop fs -rm /user/root/test/yarn.txt`

-  -rmdir：删除空目录 ` hadoop fs -mkdir /test `

-  -du统计文件夹的大小信息 ` hadoop fs -du -s -h /user/root/test`

-  -setrep：设置HDFS中文件的副本数量 `hadoop fs -setrep 10 /test/bigdata/hadoop.txt`

### 4.2 java客户端

本地需解压hadoop并配置环境变量

示例代码: https://gitee.com/ixinglan/hdfs-client-demo.git

**HDFS文件系统权限问题:**

hdfs的文件权限机制与linux系统的文件权限机制类似！

**r:read w:write x:execute** 权限x对于文件表示忽略，对于文件夹表示是否有权限访问其内容,如果linux系统用户zhangsan使用hadoop命令创建一个文件，那么这个文件在HDFS当中的owner就是zhangsan

HDFS文件权限的目的，防止好人做错事，而不是阻止坏人做坏事。HDFS相信你告诉我你是谁，你就是谁！！

解决方案:

- 指定用户信息获取FileSystem对象

- 关闭HDFS集群权限校验

  ```xml
  hdfs-site.xml 修改完成之后要分发到其它节点，同时要重启HDFS集群
  
  <property> 
    <name>dfs.permissions</name> 
    <value>true</value>
  </property>
  ```

- 基于HDFS权限本身比较鸡肋的特点，我们可以彻底放弃HDFS的权限校验，如果生产环境中,我们可以考虑借助kerberos以及sentry等安全框架来管理大数据集群安全。所以我们直接修改HDFS的根目录权限为777 `hadoop fs -chmod -R 777 / 

## 5. HDFS读写解析

- 读数据流程

  ![img_9](https://img.zhaojq.top/20260727091845583.png "img_9")

  ```
  1. 客户端通过Distributed FileSystem向NameNode请求下载文件，NameNode通过查询元数据，
  找到文件块所在的DataNode地址。
  2. 挑选一台DataNode（就近原则，然后随机）服务器，请求读取数据。
  3. DataNode开始传输数据给客户端（从磁盘里面读取数据输入流，以Packet为单位来做校验）。
  4. 客户端以Packet为单位接收，先在本地缓存，然后写入目标文件。
  ```

- 写数据流程

  ![img_10](https://img.zhaojq.top/20260727091845755.png "img_10")

  ```
  1. 客户端通过Distributed FileSystem模块向NameNode请求上传文件，NameNode检查目标文件
  是否已存在，父目录是否存在。
  2. NameNode返回是否可以上传。
  3. 客户端请求第一个 Block上传到哪几个DataNode服务器上。
  4. NameNode返回3个DataNode节点，分别为dn1、dn2、dn3。 
  5. 客户端通过FSDataOutputStream模块请求dn1上传数据，dn1收到请求会继续调用dn2，然后
  dn2调用dn3，将这个通信管道建立完成。
  6. dn1、dn2、dn3逐级应答客户端。
  7. 客户端开始往dn1上传第一个Block（先从磁盘读取数据放到一个本地内存缓存），以Packet为单
  位，dn1收到一个Packet就会传给dn2，dn2传给dn3；dn1每传一个packet会放入一个确认队列
  等待确认。
  8. 当一个Block传输完成之后，客户端再次请求NameNode上传第二个Block的服务器。（重复执行
  3-7步）。
  ```

## 6. NN与2NN

### 6.1 HDFS元数据管理机制

问题1：NameNode如何管理和存储元数据？

计算机中存储数据两种：内存或者是磁盘

- 元数据存储磁盘：存储磁盘无法面对客户端对元数据信息的任意的快速低延迟的响应，但是安全性高
- 元数据存储内存：元数据存放内存，可以高效的查询以及快速响应客户端的查询请求，数据保存在内存，如果断点，内存中的数据全部丢失。

**解决方案：内存+磁盘；NameNode内存+FsImage的文件（磁盘）**

新问题：磁盘和内存中元数据如何划分？

两个数据一模一样，还是两个数据合并到一起才是一份完整的数据呢？

- 一模一样：client如果对元数据进行增删改操作，需要保证两个数据的一致性。FsImage文件操作起来效率也不高。
- 两个合并=完整数据：NameNode引入了一个edits文件（日志文件：只能追加写入）edits文件记录的是client的增删改操作，
  不再选择让NameNode把数据dump出来形成FsImage文件（这种操作是比较消耗资源）。

**元数据管理流程图**

![img_11](https://img.zhaojq.top/20260727091845974.png "img_11")

- 第一阶段：NameNode启动

  ```
  1 第一次启动NameNode格式化后，创建Fsimage和Edits文件。
  	如果不是第一次启动，直接加载编辑日志和镜像文件到内存。
  2 客户端对元数据进行增删改的请求。
  3 NameNode记录操作日志，更新滚动日志。
  4 NameNode在内存中对数据进行增删改。
  ```

- 第二阶段：SecondaryNameNode工作

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

### 6.1 Fsimage与Edits文件

NameNode在执行格式化之后，会在/hadoop-2.9.2/data/tmp/dfs/name/current 目录下产生Fsimage与Edits文件

- Fsimage文件：是namenode中关于元数据的镜像，一般称为检查点，这里包含了HDFS文件系统所有目录以及文件相关信息（Block数量，副本数量，权限等信息）
- Edits文件：存储了客户端对HDFS文件系统所有的更新操作记录，Client对HDFS文件系统所有的更新操作都会被记录到Edits文件中（不包括查询操作）
- seen_txid：该文件是保存了一个数字，数字对应着最后一个Edits文件名的数字
- VERSION：该文件记录namenode的一些版本号信息，比如：CusterId,namespaceID等

> 问题：Fsimage中为什么没有记录块所对应DataNode？
>
> 在内存元数据中是有记录块所对应的dn信息，但是fsimage中就剔除了这个信息；HDFS集群在启动的时候会加载image以及edits文件，block对应的dn信息都没有记录，集群启动时会有一个安全模式
> （safemode）,安全模式就是为了让dn汇报自己当前所持有的block信息给nn来补全元数据。后续每隔一段时间dn都要汇报自己持有的block信息。

> 备注：Edits中只记录了更新相关的操作，查询或者下载文件并不会记录在内！！
>
> 问题：NameNode启动时如何确定加载哪些Edits文件呢？
>
> nn启动时需要加载fsimage文件以及那些没有被2nn进行合并的edits文件，nn如何判断哪些edits已经被合并了呢？
> 可以通过fsimage文件自身的编号来确定哪些已经被合并。

### 6.3 checkpoint周期

[hdfs-default.xml]

```xml
<!-- 定时一小时 --> 
<property> 
	<name>dfs.namenode.checkpoint.period</name> 
	<value>3600</value> 
</property> 

<!-- 一分钟检查一次操作次数，当操作次数达到1百万时，SecondaryNameNode执行一次 --> 
<property>
	<name>dfs.namenode.checkpoint.txns</name> 
	<value>1000000</value> 
	<description>操作动作次数</description> 
</property> 
<property> 
	<name>dfs.namenode.checkpoint.check.period</name>
  <value>60</value> 
  <description> 1分钟检查一次操作次数</description>
</property >
```

## 7. NN故障处理

NameNode故障后，HDFS集群就无法正常工作，因为HDFS文件系统的元数据需要由NameNode来管理维护并与Client交互，如果元数据出现损坏和丢失同样会导致NameNode无法正常工作进而HDFS文件系统无法正常对外提供服务。

如果元数据出现丢失损坏如何恢复呢？

- 将2NN的元数据拷贝到NN的节点下

  此种方式会存在元数据的丢失。

- 搭建HDFS的HA（高可用）集群，解决NN的单点故障问题！！（借助Zookeeper实现HA，一个Active的NameNode,一个是Standby的NameNode）

## 8.Hadoop的限额与归档以及集群安全模式

**高级命令**

- HDFS文件限额配置

  HDFS文件的限额配置允许我们以文件大小或者文件个数来限制我们在某个目录下上传的文件数量或者文件内容总量，以便达到我们类似百度网盘网盘等限制每个用户允许上传的最大的文件的量

  - 数量限额

    ```sh
    hdfs dfs -mkdir -p /user/root/lagou    #创建hdfs文件夹
    hdfs dfs admin -setQuota 2 /user/root/lagou   #给该文件夹下面设置最多上传两个文件，上传文件，发现只能上传一个文件
    hdfs dfs admin -clrQuota /user/root/lagou  #清除文件数量限制
    ```

  - 空间大小限制

    ```sh
    # 限制空间大小4KB 
    hdfs dfsadmin -setSpaceQuota 4k /user/root/lagou 
    
    #上传超过4Kb的文件大小上去提示文件超过限额 
    hdfs dfs -put /export/softwares/xxx.tar.gz /user/root/lagou 
    
    #清除空间限额
    hdfs dfsadmin -clrSpaceQuota /user/root/lagou  
    
    #查看hdfs文件限额数量 
    hdfs dfs -count -q -h /user/root/lagou
    ```

- HDFS的安全模式

  安全模式是HDFS所处的一种特殊状态，在这种状态下，文件系统只接受读数据请求，而不接受删除、修改等变更请求。在NameNode主节点启动时，HDFS首先进入安全模式，DataNode在启动的时候会向NameNode汇报可用的block等状态，当整个系统达到安全标准时，HDFS自动离开安全模式。如果HDFS出于安全模式下，则文件block不能进行任何的副本复制操作，因此达到最小的副本数量要求是基于DataNode启动时的状态来判定的，启动时不会再做任何复制（从而达到最小副本数量要求），HDFS集群刚启动的时候，默认30S钟的时间是出于安全期的，只有过了30S之后，集群脱离了安全期，然后才可以对集群进行操作。

  `hdfs dfsadmin -safemode`

- Hadoop归档技术

  主要解决HDFS集群存在大量小文件的问题！！

  由于大量小文件会占用NameNode的内存，因此对于HDFS来说存储大量小文件造成NameNode 内存资源的浪费！

  Hadoop存档文件HAR文件，是一个更高效的文件存档工具，HAR文件是由一组文件通过archive 工具创建而来，在减少了NameNode的内存使用的同时，可以对文件进行透明的访问，通俗来说就是HAR文件对NameNode来说是一个文件减少了内存的浪费，对于实际操作处理文件依然是一个一个独立的文件。![img_12](https://img.zhaojq.top/20260727091846150.png "img_12")

  ```sh
  # 把/user/lagou/input目录里面的所有文件归档成一个叫input.har的归档文件，
  # 并把归档后文件存储到/user/lagou/output路径下
  hadoop archive -archiveName input.har –p /user/root/input /user/root/output
  
  #查看归档
  hadoop fs -lsr /user/root/output/input.har
  
  #解归档文件
  hadoop fs -cp har:/// user/root/output/input.har/* /user/root
  ```

  


































