介绍

HDFS的全称是Hadoop Distributed File System ,Hadoop的分布式文件系统
它是一种允许文件通过网络在多台主机上分享的文件系统,可以让多台机器上的多个用户分享文件和存储空间

其实分布式文件管理系统有很多,HDFS只是其中一种实现而已 还有 GFS(谷歌的)、TFS(淘宝的)、S3(亚马逊的) 为什么会有多种分布式文件系统呢?这样不是重复造轮子吗? 不是的,因为不同的分布式文件系统的特点是不一样的,HDFS是一种适合大文件存储的分布式文件系统,不适合小文件存储,什么叫小文件,例如,几KB,几M的文件都可以认为是小文件

HDFS的Shell介绍

针对HDFS,我们可以在shell命令行下进行操作,就类似于我们操作linux中的文件系统一样,但是具体命令的操作格式是有一些区别的

格式如下:
image.png

使用hadoop bin目录的hdfs命令,后面指定dfs,表示是操作分布式文件系统的(如果在PATH中配置了hadoop的bin目录,那么这里可以直接使用hdfs就可以了)
xxx是一个占位符,表示对hdfs做什么操作
HDFS的schema是hdfs
authority是集群中namenode所在节点的ip和对应的端口号,把ip换成主机名也是一样的,
path是我们要操作的文件路径信息
其实后面这一长串内容就是core-site.xml配置文件中fs.defaultFS属性的值,这个代表的是HDFS的地址。

HDFS的常见Shell操作

-ls:查询指定路径信息

查看hdfs根目录下的内容,什么都不显示,因为默认情况下hdfs中什么都没有

  1. hdfs dfs -ls hdfs://bigdata1:9000/

其实后面hdfs的url这一串内容在使用时默认是可以省略的,因为hdfs在执行的时候会根据HDOOP_HOME自动识别配置文件中的fs.defaultFS属性
所以这样简写也是可以的

hdfs dfs -ls /

-put:从本地上传文件

接下来我们向hdfs中上传一个文件,使用Hadoop中的README.txt,直接上传到hdfs的根目录即可
image.png

hdfs dfs -put README.txt  /

image.png

-cat:查看HDFS文件内容

文件上传上去以后,我们还想查看一下HDFS中文件的内容,很简单,使用cat即可

hdfs dfs -cat /README.txt

image.png

-get:下载文件到本地

如果我们想把hdfs中的文件下载到本地linux文件系统中需要怎么做呢?使用get即可实现

hdfs dfs -get /README.txt aa.txt

因为本地README.txt已存在,所以命名为aa.txt
image.png

-mkdir [-p]:创建文件夹

创建test文件夹

hdfs dfs -mkdir /test

image.png
如果要递归创建多级目录,还需要再指定-p参数

hdfs dfs -mkdir -p /abc/xyz

想要递归显示所有目录的信息,可以在ls后面添加-R参数

hdfs dfs -ls -R /

image.png

-rm [-r]:删除文件/文件夹

删除文件

hdfs dfs -rm /README.txt

image.png

删除目录,注意,删除目录需要指定-r参数

hdfs dfs -rm -r /test

image.png

如果是多级目录,可以递归删除吗?可以

hdfs dfs -rm -r /abc

image.png

HDFS案例实操

需求:统计HDFS中文件的个数和每个文件的大小

我们先向HDFS中上传几个文件,把hadoop目录中的几个txt文件上传上去

hdfs dfs -put LICENSE.txt /
hdfs dfs -put NOTICE.txt /
hdfs dfs -put README.txt /

image.png
1:统计根目录下文件的个数

hdfs dfs -ls / |grep /| wc -l

image.png
2、统计根目录下每个文件的大小,最终把文件名称和大小打印出来

hdfs dfs -ls / |grep / |  awk '{print $8,$5}'

image.png

Java代码操作HDFS

<dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-client</artifactId>
    <version>3.2.0</version>
</dependency>
/**
 * Java代码操作HDFS
 * 文件操作:上传文件,下载文件,删除文件
 */
public class HdfsOp {

    public static void main(String[] args) throws Exception {
        //创建一个配置对象
        Configuration conf = new Configuration();
        //指定HDFS的地址
        conf.set("fs.defaultFS", "hdfs://192.168.1.21:9000");
        //获取操作HDFS的对象
        FileSystem fileSystem = FileSystem.get(conf);

        //上传文件
        //put(fileSystem);
        //下载文件
        //get(fileSystem);
        //删除文件
        delete(fileSystem);

    }
}

上传文件

    /**
     * 上传文件
     *
     * @param fileSystem
     * @throws IOException
     */
    private static void put(FileSystem fileSystem) throws IOException {
        //获取本地文件的输入流
        FileInputStream fis = new FileInputStream("D:\\user.txt");
        //获取HDFS文件系统的输出流
        FSDataOutputStream fos = fileSystem.create(new Path("/user.txt"));
        //上传文件:通过工具类把输入流拷贝到输出流里面,实现本地文件上传到HDFS
        IOUtils.copyBytes(fis, fos, 1024, true);
    }

执行代码,发现报错,提示权限拒绝,说明windows中的这个用户没有权限向HDFS中写入数据image.png
解决办法有两个
第一种:去掉hdfs的用户权限检验机制,通过在hdfs-site.xml中配置dfs.permissions.enabled为false即可
第二种:把代码打包到linux中执行
在这里为了在本地测试方便,我们先使用第一种方式
1:停止Hadoop集群

stop-all.sh

2:修改hdfs-site.xml配置文件

cd etc/hadoop/
vim hdfs-site.xml

伪分布式集群配置:

<configuration>
    <property>
        <name>dfs.replication</name>
        <value>1</value>
    </property>


    <property>
        <name>dfs.permissions.enabled</name>
        <value>false</value>
    </property>
</configuration>

分布式集群配置:

注意:集群内所有节点中的配置文件都需要修改,先在bigdata01节点上修改,然后再同步到另外两个节点上

<configuration>
    <property>
        <name>dfs.replication</name>
        <value>2</value>
    </property>
    <property>
        <name>dfs.namenode.secondary.http-address</name>
        <value>bigdata01:50090</value>
    </property>
    <property>
        <name>dfs.permissions.enabled</name>
        <value>false</value>
    </property>
</configuration>

同步到另外两个节点中

scp -rq etc/hadoop/hdfs-site.xml  bigdata2:/data/soft/hadoop-3.2.0/etc/hadoop/
scp -rq etc/hadoop/hdfs-site.xml  bigdata3:/data/soft/hadoop-3.2.0/etc/hadoop/

3、启动Hadoop集群

start-all.sh

重新再执行代码,没有报错,到hdfs上查看数据
image.png

下载文件

    /**
     * 下载文件
     *
     * @param fileSystem
     * @throws IOException
     */
    private static void get(FileSystem fileSystem) throws IOException {
        //获取HDFS文件系统中的输入流
        FSDataInputStream fis = fileSystem.open(new Path("/README.txt"));
        //获取本地文件的输出流
        FileOutputStream fos = new FileOutputStream("D:\\README.txt");
        //下载文件
        IOUtils.copyBytes(fis, fos, 1024, true);
    }

image.png

删除文件

    /**
     * 删除文件或者目录
     *
     * @param fileSystem
     * @throws IOException
     */
    private static void delete(FileSystem fileSystem) throws IOException {
        //删除文件,目录也可以删除
        //如果要递归删除目录,则第二个参数需要设置为true
        //如果删除的是文件或者空目录,第二个参数会被忽略
        boolean flag = fileSystem.delete(new Path("/user.txt"), true);
        if (flag) {
            System.out.println("删除成功!");
        } else {
            System.out.println("删除失败!");
        }
    }

image.png

使用WEB-UI操作HDFS

web地址:http://192.168.1.21:9870/
image.png

解决Couldn’t upload the file XXX.问题
image.png
原因 : win10的hosts没有配置ip映射,无法解析域名
在win10上配置ip 域名映射
image.png

HDFS体系结构

image.png
NameNode、SecondaryNameNode、DataNode是HDFS服务的进程,
ResourceManager、NodeManager是YARN服务的进程。

HDFS支持主从结构,主节点称为 NameNode ,NameNode支持多个。
从节点称为 DataNode ,DataNode支持多个。

可以这样理解: 公司BOSS:NameNode 秘书:SecondaryNameNode 员工:DataNode

接着看一下这张图,这就是HDFS的体系结构,这里面的TCP、RPC、HTTP表示是不同的网络通信方式,通过这张图是想加深大家对HDFS体系结构的理解,我们前面配置的集群NameNode和SecondaryNameNode进程在同一台机器上面,在这个图里面是把它们分开到多台机器中了。

image.png

NameNode(管理节点)

NameNode是整个文件系统的管理节点
它主要维护着整个文件系统的文件目录树,文件/目录的信息 和 每个文件对应的数据块列表,并且还负责接收用户的操作请求。

  • 目录树:表示目录之间的层级关系,就是我们在hdfs上执行ls命令可以看到的那个目录结构信息。
  • 文件/目录的信息:表示文件/目录的的一些基本信息,所有者 属组 修改时间 文件大小等信息
  • 每个文件对应的数据块列表:如果一个文件太大,那么在集群中存储的时候会对文件进行切割,这个时候就类似于会给文件分成一块一块的,存储到不同机器上面。所以HDFS还要记录一下一个文件到底被分了多少块,每一块都在什么地方存储着

我们现在可以到集群的9870界面查看一下,随便找一个文件看一下,点击文件名称,可以看到Block information 但是文件太小,只有一个块 叫Block 0
image.png
我们试着上传一个大一点的文件,找一个200M左右的文件。

[root@bigdata01 hadoop-3.2.0]# cd /data/soft/
[root@bigdata01 soft]# hdfs dfs -put hadoop-3.2.0.tar.gz  /

image.png
NameNode主要包括以下文件:

image.png

fsimage 元数据镜像文件,存储某一时刻NameNode内存中的元数据信息,就类似是定时做了一个快照操作。【这里的元数据信息是指文件目录树、文件/目录的信息、每个文件对应的数据块列表】
edits 操作日志文件【事务文件】,这里面会实时记录用户的所有操作
seentxid 是存放transactionId的文件,format之后是0,它代表的是namenode里面的edits*文件的尾数,namenode重启的时候,会按照seen_txid的数字,顺序从头跑edits_0000001~到seen_txid的数字。如果根据对应的seen_txid无法加载到对应的文件,NameNode进程将不会完成启动以保护数据一致性。
VERSION 保存了集群的版本信息

SecondaryNameNode

SecondaryNameNode主要负责定期的把edits文件中的内容合并到fsimage中
这个合并操作称为checkpoint,在合并的时候会对edits中的内容进行转换,生成新的内容保存到fsimage文件中。
注意:在NameNode的HA架构中没有SecondaryNameNode进程,文件合并操作会由standby NameNode负责实现
所以在Hadoop集群中,SecondaryNameNode进程并不是必须的

DataNode(真实文件存储)

DataNode是提供真实文件数据的存储服务
针对datanode主要掌握两个概念,一个是block,一个是replication

block(128MB)

HDFS会按照固定的大小,顺序对文件进行划分并编号,划分好的每一个块称一个Block,HDFS默认Block大小是 128MB。
Blokc块是HDFS读写数据的基本单位,不管你的文件是文本文件 还是视频 或者音频文件,针对hdfs而言 都是字节。

我们之前上传的一个user.txt文件,他的block信息可以在fsimage文件中看到,也可以在hdfs webui上面看到, 里面有block的id信息,并且也会显示这个数据在哪个节点上面
image.png
这里显示在bigdata02和bigdata03上面都有,那我们过去看一下,datanode中数据的具体存储位置是由dfs.datanode.data.dir来控制的,通过查询hdfs-default.xml可以知道,具体的位置在这里

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

那我们连接到bigdata02这个节点上去看一下

[root@bigdata02 ~]# cd /data/hadoop_repo/dfs/data/
[root@bigdata02 data]# ll
total 4
drwxr-xr-x. 3 root root 72 Apr  7 22:21 current
-rw-r--r--. 1 root root 14 Apr  8 20:30 in_use.lock

然后进入current目录,继续一路往下走

[root@bigdata02 data]# cd current/
[root@bigdata02 current]# ll
total 4
drwx------. 4 root root  54 Apr  8 20:30 BP-1517789416-192.168.182.100-1586268855170
-rw-r--r--. 1 root root 229 Apr  8 20:30 VERSION
[root@bigdata02 current]# cd BP-1517789416-192.168.182.100-1586268855170/
[root@bigdata02 BP-1517789416-192.168.182.100-1586268855170]# ll
total 4
drwxr-xr-x. 4 root root  64 Apr  8 20:25 current
-rw-r--r--. 1 root root 166 Apr  7 22:21 scanner.cursor
drwxr-xr-x. 2 root root   6 Apr  8 20:30 tmp
[root@bigdata02 BP-1517789416-192.168.182.100-1586268855170]# cd current/
[root@bigdata02 current]# ll
total 8
-rw-r--r--. 1 root root  20 Apr  8 20:25 dfsUsed
drwxr-xr-x. 3 root root  21 Apr  8 15:34 finalized
drwxr-xr-x. 2 root root   6 Apr  8 22:13 rbw
-rw-r--r--. 1 root root 146 Apr  8 20:30 VERSION
[root@bigdata02 current]# cd finalized/
[root@bigdata02 finalized]# ll
total 0
drwxr-xr-x. 3 root root 21 Apr  8 15:34 subdir0
[root@bigdata02 finalized]# cd subdir0/
[root@bigdata02 subdir0]# ll
total 4
drwxr-xr-x. 2 root root 4096 Apr  8 22:13 subdir0
[root@bigdata02 subdir0]# cd subdir0/
[root@bigdata02 subdir0]# ll
total 340220
-rw-r--r--. 1 root root     22125 Apr  8 15:55 blk_1073741828
-rw-r--r--. 1 root root       183 Apr  8 15:55 blk_1073741828_1004.meta
-rw-r--r--. 1 root root      1361 Apr  8 15:55 blk_1073741829
-rw-r--r--. 1 root root        19 Apr  8 15:55 blk_1073741829_1005.meta
-rw-r--r--. 1 root root        17 Apr  8 20:31 blk_1073741830
-rw-r--r--. 1 root root        11 Apr  8 20:31 blk_1073741830_1006.meta
-rw-r--r--. 1 root root 134217728 Apr  8 22:13 blk_1073741831
-rw-r--r--. 1 root root   1048583 Apr  8 22:13 blk_1073741831_1007.meta
-rw-r--r--. 1 root root 134217728 Apr  8 22:13 blk_1073741832
-rw-r--r--. 1 root root   1048583 Apr  8 22:13 blk_1073741832_1008.meta
-rw-r--r--. 1 root root  77190019 Apr  8 22:13 blk_1073741833
-rw-r--r--. 1 root root    603055 Apr  8 22:13 blk_1073741833_1009.meta

这里面就有很多的block块了,

注意: 这里面的.meta文件也是做校验用的。

根据前面看到的blockid信息到这对应的找到文件,可以直接查看,发现文件内容就是我们之前上传上去的内容。

[root@bigdata02 subdir0]# cat blk_1073741830
jack
tom
jessic
[root@bigdata02 subdir0]#

注意:这个block中的内容可能只是文件的一部分,如果你的文件较大的话,就会分为多个block存储,默认 hadoop3中一个block的大小为128M。根据字节进行截取,截取到128M就是一个block。如果文件大小没有默认的block块大,那最终就只有一个block。

HDFS中,如果一个文件小于一个数据块的大小,那么并不会占用整个数据块的存储空间

假设我们上传了两个10M的文件 又上传了一个200M的文件
问1:会产生多少个block块? 4个
问2:在hdfs中会显示几个文件?3个

replication

副本表示数据有多少个备份

我们现在的集群有两个从节点,所以最多可以有2个备份,这个是在hdfs-site.xml中进行配置的,dfs.replication
默认这个参数的配置是3。表示会有3个副本。

副本只有一个作用就是保证数据安全。

总结

注意:block块存放在哪些datanode上,只有datanode自己知道,当集群启动的时候,datanode会扫描自己节点上面的所有block块信息,然后把节点和这个节点上的所有block块信息告诉给namenode。这个关系是每次重启集群都会动态加载的【这个其实就是集群为什么数据越多,启动越慢的原因】

namenode维护了两份关系:
第一份关系:file 与block list的关系,对应的关系信息存储在fsimage和edits文件中,当NameNode启动的时候会把文件中的元数据信息加载到内存中
第二份关系:datanode与block的关系,对应的关系主要在集群启动的时候保存在内存中,当DataNode启动时会把当前节点上的Block信息和节点信息上报给NameNode

注意了,刚才我们说了NameNode启动的时候会把文件中的元数据信息加载到内存中,然后每一个文件的元数据信息会占用150字节的内存空间,这个是恒定的,和文件大小没有关系,咱们前面在介绍HDFS的时候说过,HDFS不适合存储小文件,其实主要原因就在这里,不管是大文件还是小文件,一个文件的元数据信息在NameNode中都会占用150字节,NameNode节点的内存是有限的,所以它的存储能力也是有限的,如果我们存储了一堆都是几KB的小文件,最后发现NameNode的内存占满了,确实存储了很多文件,但是文件的总体大小却很小,这样就失去了HDFS存在的价值

HDFS的回收站

我们windows系统里面有一个回收站,当想恢复删除的文件的话就可以到这里面进行恢复,HDFS也有回收站。
HDFS会为每一个用户创建一个回收站目录:/user/用户名/.Trash/,每一个被用户在Shell命令行删除的文件/目录,会进入到对应的回收站目录中,在回收站中的数据都有一个生存周期,也就是当回收站中的文件/目录在一段时间之内没有被用户恢复的话,HDFS就会自动的把这个文件/目录彻底删除,之后,用户就永远也找不回这个文件/目录了。
默认情况下hdfs的回收站是没有开启的,需要通过一个配置来开启,在core-site.xml中添加如下配置,value的单位是分钟,1440分钟表示是一天的生存周期

<property>
    <name>fs.trash.interval</name>
    <value>1440</value>
</property>

在修改配置信息之前先验证一下删除操作,显示的是直接删除掉了。

[root@bigdata01 hadoop-3.2.0]# hdfs dfs -rm -r /NOTICE.txt
Deleted /NOTICE.txt

修改回收站配置,先在bigdata01上操作,然后再同步到其它两个节点,先停止集群

[root@bigdata01 hadoop-3.2.0]# sbin/stop-all.sh 
[root@bigdata01 hadoop-3.2.0]# vim etc/hadoop/core-site.xml 
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://bigdata01:9000</value>
    </property>
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/data/hadoop_repo</value>
   </property>
    <property>
        <name>fs.trash.interval</name>
        <value>1440</value>
    </property>
</configuration>
[root@bigdata01 hadoop-3.2.0]# scp -rq etc/hadoop/core-site.xml bigdata02:/data/soft/hadoop-3.2.0/etc/hadoop/
[root@bigdata01 hadoop-3.2.0]# scp -rq etc/hadoop/core-site.xml bigdata03:/data/soft/hadoop-3.2.0/etc/hadoop/

启动集群,再执行删除操作

[root@bigdata01 hadoop-3.2.0]# sbin/start-all.sh
[root@bigdata01 hadoop-3.2.0]# hdfs dfs -rm -r /README.txt
2020-04-09 11:43:47,664 INFO fs.TrashPolicyDefault: Moved: 'hdfs://bigdata01:9000/README.txt' to trash at: hdfs://bigdata01:9000/user/root/.Trash/Current/README.txt

此时看到提示信息说把删除的文件移到到了指定目录中,其实就是移动到了当前用户的回收站目录。
回收站的文件也是可以下载到本地的。其实在这回收站只是一个具备了特殊含义的HDFS目录。

注意:如果删除的文件过大,超过回收站大小的话会提示删除失败 需要指定参数 -skipTrash ,指定这个参数表示删除的文件不会进回收站

[root@bigdata01 hadoop-3.2.0]# hdfs dfs -rm -r -skipTrash /user.txt
Deleted /user.txt

HDFS的安全模式

大家在平时操作HDFS的时候,有时候可能会遇到这个问题,特别是刚启动集群的时候去上传或者删除文件,会发现报错,提示NameNode处于safe mode。
这个属于HDFS的安全模式,因为在集群每次重新启动的时候,HDFS都会检查集群中文件信息是否完整,例如副本是否缺少之类的信息,所以这个时间段内是不允许对集群有修改操作的,如果遇到了这个情况,可以稍微等一会,等HDFS自检完毕,就会自动退出安全模式。

[root@bigdata01 hadoop-3.2.0]# hdfs dfs -rm -r /hadoop-3.2.0.tar.gz
2020-04-09 12:00:36,646 WARN fs.TrashPolicyDefault: Can't create trash directory: hdfs://bigdata01:9000/user/root/.Trash/Current
org.apache.hadoop.hdfs.server.namenode.SafeModeException: Cannot create directory /user/root/.Trash/Current. Name node is in safe mode.

此时访问HDFS的web ui界面,可以看到下面信息,on表示处于安全模式,off表示安全模式已结束
image.png
或者通过hdfs命令也可以查看当前的状态

[root@bigdata01 hadoop-3.2.0]# hdfs dfsadmin -safemode get
Safe mode is ON

如果想快速离开安全模式,可以通过命令强制离开,正常情况下建议等HDFS自检完毕,自动退出

[root@bigdata01 hadoop-3.2.0]# hdfs dfsadmin -safemode leave
Safe mode is OFF

此时,再操作HDFS中的文件就可以了。