Hadoop之HDFS

一.HDFS概述

1.1HDFS产生的背景定义


1)背景:随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到更多的 操作系统管理的磁盘中,但是不方便管理和维护,迫切需要一种系统来管理多台机器上的文件,这就是分布式文件管理系统。HDFS只是分布式文件管理系统中的一种。
2)定义:HDFS(Hadoop Distributed File System),它是一个文件系统,用于存储文件,通过目录树来定位文件;其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。
HDFS的使用场景:适合一次写入,多次读出的场景。一个文件经过创建、写入和关闭之后就不需要改变。

1.2 HDFS优缺点

1)优点:
1、可构建在廉价机器上
通过多个副本来提高可靠性,文件切分多个块进行存储
2、高容错性
数据自动保存多个副本,副本丢失后,可以自动恢复
3、适合批处理
移动计算比移动数据方便
4、适合大数据处理
10k+节点规模
5、流式文件访问
一次写入,多次读取,可以保证数据的一致性
2)缺点:
不适于以下操作
1、要求高的数据访问
比如毫秒级
2、小文件存取
寻道时间超过读取时间
3、并发写入、文件随机修改
一个文件只能有一个写
仅仅支持追加
4、不适合存储小文件
存储一个1亿个小文件,大小仅仅1t,但是消耗掉20g左右的内存

1.3HDFS组成架构

 **1.3.1架构介绍**

在这里插入图片描述HDFS是一个主从(Master/Slaves)架构
由一个NameNode和一些DataNode组成
面向文件包含:文件数据(data)和文件元数据(metadata)
NameNode负责存储和管理文件元数据,并维护了一个层次型的文件目录树
DataNode负责存储文件数据(block块),并提供block的读写
DataNode与NameNode维持心跳,并汇报自己持有的block信息
Client和NameNode交互文件元数据和DataNode交互文件block数据

1.3.2角色功能
1) NameNode
完全基于内存存储文件元数据、目录结构、文件block的映射
需要持久化方案保证数据可靠性
提供副本放置策略
2)DataNode
基于本地磁盘存储block(文件的形式)
并保存block的校验和数据保证block的可靠性
与NameNode保持心跳,汇报block列表状态
3) Client:就是客户端。
文件切分。文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行上传;(2)与NameNode交互,获取文件的位置信息;
与DataNode交互,读取或者写入数据;
Client提供一些命令来管理IDFS,比INameNode格式化;
Client可以通过一些命令来访问HIDFS,比对HHDFSt增删查改操作;
4) Secondary NameNode:并非NameNode的热备。当NameNode挂掉的时候,它并不能马上替换NameNode并提供服务。
在非Ha模式下,SNN一般是独立的节点,周期完成对NN的EditLog向FsImage合并,减少EditLog大小,减少NN启动时间
根据配置文件设置的时间间隔fs.checkpoint.period 默认3600秒
根据配置文件设置edits log大小 fs.checkpoint.size 规定edits文件的最大值默认是64MB
1.3.3 HDFS文件块大小
在这里插入图片描述
举个例子:
100g的数据,集群有100个节点,按照1g的大小进行存储,每个节点要存储1g的数据量
100g的数据,集群有90个节点,按照1g的大小进行存储,其中有10台要存储2g的数据,其他的80台要存储1g的数据
假设1g的数据需要1秒钟的运算时间,那么整个任务需要2秒钟的运算时间。
100g的数据,集群有90个节点,按照512m的大小进行切分存储,有20个节点存储1.5g的数据,有70个节点存储1g的数据
假设1g的数据需要1秒钟的运算时间,那么整个任务需要1.5秒钟的运算时间。
从上面的这个例子结果来看,我们切分是不是越小越好?
但是有个问题:小文件很多,就会有问题!
举个例子:
access.log 100g
block0 50g
block1 50g
access.log 100g
block0 20g
block1 20g
block2 20g
block3 20g
block4 20g
从上面的这个例子结果来看:因为下载的时候需要将文件还原,需要合并块,从这看,是不是切分的越大越好?
不大不小:
HDFS在设计的时候考虑了不同的应用场景,每个不同的场景中,有可能需要的切分的块的大小不一样,可以配置。
HDFS集群块大小可以配置
但是有默认的大小:
Hadoop2.x版本以前,默认块大小:64M
Hadoop2.x版本及以后,默认块大小:128M

二.HDFS的Shell操作

2.1 基本语法
hadoop fs 具体命令 OR hdfs dfs 具体命令,两个是完全相同的。
2.2 命令大全

[wangzhao@hadoop102 ~]$ hadoop fs
Usage: hadoop fs [generic options]
	[-appendToFile <localsrc> ... <dst>]
	[-cat [-ignoreCrc] <src> ...]
	[-checksum <src> ...]
	[-chgrp [-R] GROUP PATH...]
	[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
	[-chown [-R] [OWNER][:[GROUP]] PATH...]
	[-copyFromLocal [-f] [-p] [-l] [-d] [-t <thread count>] <localsrc> ... <dst>]
	[-copyToLocal [-f] [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
	[-count [-q] [-h] [-v] [-t [<storage type>]] [-u] [-x] [-e] <path> ...]
	[-cp [-f] [-p | -p[topax]] [-d] <src> ... <dst>]
	[-createSnapshot <snapshotDir> [<snapshotName>]]
	[-deleteSnapshot <snapshotDir> <snapshotName>]
	[-df [-h] [<path> ...]]
	[-du [-s] [-h] [-v] [-x] <path> ...]
	[-expunge]
	[-find <path> ... <expression> ...]
	[-get [-f] [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
	[-getfacl [-R] <path>]
	[-getfattr [-R] {-n name | -d} [-e en] <path>]
	[-getmerge [-nl] [-skip-empty-file] <src> <localdst>]
	[-head <file>]
	[-help [cmd ...]]
	[-ls [-C] [-d] [-h] [-q] [-R] [-t] [-S] [-r] [-u] [-e] [<path> ...]]
	[-mkdir [-p] <path> ...]
	[-moveFromLocal <localsrc> ... <dst>]
	[-moveToLocal <src> <localdst>]
	[-mv <src> ... <dst>]
	[-put [-f] [-p] [-l] [-d] <localsrc> ... <dst>]
	[-renameSnapshot <snapshotDir> <oldName> <newName>]
	[-rm [-f] [-r|-R] [-skipTrash] [-safely] <src> ...]
	[-rmdir [--ignore-fail-on-non-empty] <dir> ...]
	[-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
	[-setfattr {-n name [-v value] | -x name} <path>]
	[-setrep [-R] [-w] <rep> <path> ...]
	[-stat [format] <path> ...]
	[-tail [-f] [-s <sleep interval>] <file>]
	[-test -[defsz] <path>]
	[-text [-ignoreCrc] <src> ...]
	[-touch [-a] [-m] [-t TIMESTAMP ] [-c] <path> ...]
	[-touchz <path> ...]
	[-truncate [-w] <length> <path> ...]
	[-usage [cmd ...]]

Generic options supported are:
-conf <configuration file>        specify an application configuration file
-D <property=value>               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 <local|resourcemanager:port>  specify a ResourceManager
-files <file1,...>                specify a comma-separated list of files to be copied to the map reduce cluster
-libjars <jar1,...>               specify a comma-separated list of jar files to be included in the classpath
-archives <archive1,...>          specify a comma-separated list of archives to be unarchived on the compute machines

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

2.3常用命令操作
1)-help:输出这个命令参数

[wangzhao@hadoop102 ~]$  hadoop fs -help rm
-rm [-f] [-r|-R] [-skipTrash] [-safely] <src> ... :
  Delete all files that match the specified file pattern. Equivalent to the Unix
  command "rm <src>"
                                                                                 
  -f          If the file does not exist, do not display a diagnostic message or 
              modify the exit status to reflect an error.                        
  -[rR]       Recursively deletes directories.                                   
  -skipTrash  option bypasses trash, if enabled, and immediately deletes <src>.  
  -safely     option requires safety confirmation, if enabled, requires          
              confirmation before deleting large directory with more than        
              <hadoop.shell.delete.limit.num.files> files. Delay is expected when
              walking over large directory recursively to count the number of    
              files to be deleted before the confirmation. 

2)创建/wz文件夹`

[wangzhao@hadoop102 ~]$ hadoop fs -mkdir /wz

3)上传

  1. -moveFromLocal:从本地剪切粘贴到HDFS
hadoop fs  -moveFromLocal  ./wz.txt  /wz
  1. -copyFromLocal:从本地文件系统中拷贝文件到HDFS路径去
hadoop fs -copyFromLocal wz.txt /wz

3)-put:等同于copyFromLocal,生产环境更习惯用put

hadoop fs -put ./wz.txt /wz
  1. -appendToFile:追加一个文件到已经存在的文件末尾
hadoop fs -appendToFile wz.txt /wz/wz.txt

4)下载
1)-copyToLocal:从HDFS拷贝到本地

 hadoop fs -copyToLocal /wz/wz.txt ./
  1. -get:等同于copyToLocal,生产环境更习惯用get
hadoop fs -get /wz/wz.txt ./wz.txt

5)操作
1)-ls: 显示目录信息

 hadoop fs -ls /wz

2)-cat:显示文件内容

hadoop fs -cat /wz/wz.txt

3)-chgrp、-chmod、-chown:Linux文件系统中的用法一样,修改文件所属权限

hadoop fs  -chmod 777  /wz/wz.txt
hadoop fs  -chown  wangzhao:wangzhao   /wz/wz.txt

4)-rm:删除文件或文件夹

hadoop fs -rm /wz/wz.txt

5)-rm -r:递归删除目录及目录里面内容

hadoop fs -rm -r /wz

6)-mkdir:创建路径

hadoop fs -mkdir /wxh

7)从HDFS的一个路径拷贝到HDFS的另一个路径

hadoop fs -cp /wz/wz.txt  /jinguo

8)-mv:在HDFS目录中移动文件

hadoop fs -mv /wz/wy.txt /jinguo

三. HDFS的API操作

package com.wangzhao.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.yarn.webapp.hamlet2.Hamlet;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
/*
客户端代码常用套路
  1.获取客户端对象
  2.执行相关的操作
  3.关闭资源
  HDFS   Zookeeper
 */
public class HdfsClient {
    private FileSystem fs;
    @Before
    public void init() throws URISyntaxException, IOException, InterruptedException {
        //链接集群nn的地址
        URI uri = new URI("hdfs://hadoop102:8020");
        //创建一个配置文件
        Configuration conf = new Configuration();
        //用户
        String user ="wangzhao";

        //1.获取客户端对象
        fs = FileSystem.get(uri, conf,user);
    }
    @After
    public void close() throws IOException {
        //3.关闭资源
        fs.close();
    }

    //创建目录
    @Test
    public void testmkdir() throws  IOException {
        fs.mkdirs(new Path("/xiyou/huaguoshan1"));
    }

    //上传
    /*
    参数优先级   hdfs-default.xml=>hdfs-site.xml=>在项目资源目录文件下的配置文件=》代码里面的配置
     */
    @Test
    public  void testPut() throws IOException {
        //参数一表示删除原数据 参数二 是否允许被覆盖   参数三 原数据路径  参数四目的地路径
        fs.copyFromLocalFile(true,false,new Path("D:\sunwukong.txt"),new Path("/xiyou/huaguoshan"));
    }
    //文件下载
    @Test
    public void testGet() throws IOException {
        // 参数一:原文件是否删除 参数二:原文件路径(HDFS) 参数三:目标地址路径(Win) 参数四:校验文件
        fs.copyToLocalFile(true,new Path("hdfs://hadoop102/xiyou/huaguoshan"),new Path("D:\sunwukong.txt"),true);
    }
    //删除
    @Test
    public void testRm() throws IOException {
        //参数一:要删除的路径 参数二:是否要递归删除
        //fs.delete(new Path("/xiyou/huaguoshan1/sunwukong.txt"),false);
        //删除空目录
        fs.delete(new Path("/xiyou/huaguoshan1"),false);
    }
    //文件的更名和移动
    @Test
    public void testMv() throws IOException {
        //参数一原文件路径  参数二 目标文件的路径
        //对文件名称的修改
        //fs.rename(new Path("/xiyou/huaguoshan"),new Path("/xiyou/shuilian"));

        //文件的移动和更名
        fs.rename(new Path("/xiyou/shuilian"),new Path("/cls"));
    }
    //获取文件详情
    @Test
    public void fileDetail() throws IOException {
        //获取所有文件信息
        RemoteIterator<LocatedFileStatus>  list_f = fs.listFiles(new Path("/"), true);
        while (list_f.hasNext()){
            LocatedFileStatus  fileStatus = list_f.next();
            System.out.println("----------"+fileStatus.getPath()+"---------------");
            System.out.println(fileStatus.getPermission());
            System.out.println(fileStatus.getOwner());
            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());
            //获取块信息
            System.out.println("获取块信息");
            BlockLocation[] locations = fileStatus.getBlockLocations();
            System.out.println(Arrays.toString(locations));
        }
    }
    //判断是文件还是文件夹
    @Test
    public void testFile() throws IOException {
        FileStatus[] list = fs.listStatus(new Path("/"));
        for (FileStatus status : list) {
            if (status.isFile()) {
                System.out.println("     文件 :    "+status.getPath().getName());
            }else {
                System.out.println("目录:"+status.getPath().getName());
            }
        }
    }
}

四.HDFS读写流程(重点)

1)Block的副本放置策略
第一个副本:放置在上传文件的DN;如果是集群外提交,则随机挑选一台磁盘不太满,CPU不太忙的节点。
第二个副本:放置在于第一个副本不同的 机架的节点上。
第三个副本:与第二个副本相同机架的节点。
更多副本:随机节点。
2)写流程
在这里插入图片描述
Client和NN连接创建文件元数据
NN判定元数据是否有效
NN处发副本放置策略,返回一个有序的DN列表
Client和DN建立Pipeline连接
Client将块切分成packet(64KB),并使用chunk(512B)+chucksum(4B)填充
Client将packet放入发送队列dataqueue中,并向第一个DN发送
第一个DN收到packet后本地保存并发送给第二个DN
第二个DN收到packet后本地保存并发送给第三个DN
这一个过程中,上游节点同时发送下一个packet
生活中类比工厂的流水线
Hdfs使用这种传输方式,副本数对于client是透明的
当block传输完成,DN们各自向NN汇报,同时client继续传输下一个block
所以,client的传输和block的汇报也是并行的
3)读流程
在这里插入图片描述
为了降低整体的带宽消耗和读取延时,HDFS会尽量让读取程序读取离它最近的副本。
如果在读取程序的同一个机架上有一个副本,那么就读取该副本。
如果一个HDFS集群跨越多个数据中心,那么客户端也将首先读本地数据中心的副本。
语义:下载一个文件:
Client和NN交互文件元数据获取fileBlockLocation
NN会按距离策略排序返回
Client尝试下载block并校验数据完整性
语义:下载一个文件其实是获取文件的所有的block元数据,那么子集获取某些block应该成立
Hdfs支持client给出文件的offset自定义连接哪些block的DN,自定义获取数据
这个是支持计算层的分治、并行计算的核心

五.NameNode和SecondaryNameNode

1)NN和2NN工作机制
思考:NameNode中的元数据是存储在哪里的?
首先,我们做个假设,如果存储在NameNode节点的磁盘中,因为经常需要进行随机访问,还有响应客户请求,必然是效率过低。因此,元数据需要存放在内存中。但如果只存在内存中,一旦断电,元数据丢失,整个集群就无法工作了。因此产生在磁盘中备份元数据的FsImage。
这样又会带来新的问题,当在内存中的元数据更新时,如果同时更新FsImage,就会导致效率过低,但如果不更新,就会发生一致性问题,一旦NameNode节点断电,就会产生数据丢失。因此,引入Edits文件(只进行追加操作,效率很高)。每当元数据有更新或者添加元数据时,修改内存中的元数据并追加到Edits中。这样,一旦NameNode节点断电,可以通过FsImage和Edits的合并,合成元数据。
但是,如果长时间添加数据到Edits中,会导致该文件数据过大,效率降低,而且一旦断电,恢复元数据需要的时间过长。因此,需要定期进行FsImage和Edits的合并,如果这个操作由NameNode节点完成,又会效率过低。因此,引入一个新的节点SecondaryNamenode,专门用于FsImage和Edits的合并。
在这里插入图片描述

1)第一阶段:NameNode启动
(1)第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
(2)客户端对元数据进行增删改的请求。
(3)NameNode记录操作日志,更新滚动日志。
(4)NameNode在内存中对元数据进行增删改。
2)第二阶段:Secondary NameNode工作
(1)Secondary NameNode询问NameNode是否需要CheckPoint。直接带回NameNode是否检查结果。
(2)Secondary NameNode请求执行CheckPoint。
(3)NameNode滚动正在写的Edits日志。
(4)将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode。
(5)Secondary NameNode加载编辑日志和镜像文件到内存,并合并。
(6)生成新的镜像文件fsimage.chkpoint。
(7)拷贝fsimage.chkpoint到NameNode。
(8)NameNode将fsimage.chkpoint重新命名成fsimage。

六.HDFS启动过程

1)启动流程
1、先启动namenode进程
2、加载namenode文件夹存储的磁盘元数据(fsimage + edits_inprogress )
3、namenode在启动完毕之后,会在namenode节点上启动一个服务
服务的作用:等待所有的datanode的上线,datanode汇报他们的数据块的内容
4、datanode一旦上线,就会通过心跳机制把自身所持有的块信息全部汇报给namenode
5、只有namenode等到了所有的datanode的上线以及把所有的块信息汇报完毕之后,这个时候namenode才能知道所有文件的副本的存放位置

元数据:描述数据的数据
包含:
1、目录数据结构
2、每个文件的数据块的存储的节点位置
集群很大,就会出现一些情况:
1、元数据fsimage文件越来越大,加载到内存所需要的时间越来越长。
2、datanode节点多,每个节点保存的数据库的个数也多。
结论:随着HDFS集群的增大,HDFS集群启动消耗的时间越来越长。
那就有问题:
在HDFS没有完全启动的情况下,客户端可不可以进行正常的上传下载操作?? 不可以。
在正常启动的时间范围内,HDFS集群处于安全模式!!
汇报:是datanode给namenode发信息
命令:是namenode给datanode发信息
都是用的同一套的心跳机制。
namenode判断一个datanode的存活状态:超时时长的标准
timeout(超时时长) = 10 * 心跳时长 + 2 * 检查心跳机制是否正常工作的时间
参数: 心跳时长:dfs.heartbeat.interval 3秒钟
检查心跳机制是否正常工作的时间 :heartbeat.recheck.interval 5分钟
所以: timeout= 103 + 2300 = 630秒。
在hdfs-site.xml文件中可以配置修改。

2)安全模式
Namenode启动后会进入一个称为安全模式的特殊状态。
处于安全模式的Namenode是不会进行数据块的复制的。
Namenode从所有的 Datanode接收心跳信号和块状态报告。
每当Namenode检测确认某个数据块的副本数目达到这个最小值,那么该数据块就会被认为是副本安全(safely replicated)的。
在一定百分比(这个参数可配置)的数据块被Namenode检测确认是安全之后(加上一个额外的30秒等待时间),Namenode将退出安全模式状态。
接下来它会确定还有哪些数据块的副本没有达到指定数目,并将这些数据块复制到其他Datanode上

进入安全模式的情况
1、当HDFS集群中的部分datanode节点宕机之后,HDFS后台会启动一些服务,做自我恢复。
2、当丢失的数据块比例超过0.1%的时候就会自动进入安全模式。

安全模式,有以下命令:
hdfs dfsadmin -safemode leave //强制退出安全模式
hdfs dfsadmin -safemode enter //进入安全模式  
hdfs dfsadmin -safemode get //查看安全模式
[root@hadoop0 ~]# hdfs dfsadmin -safemode get//查看安全模式
Safe mode is OFF
hdfs dfsadmin -safemode wait //等待,一直到安全模式结束为止

退出安全模式的情况
1、使用命令强制退出
hdfs dfsadmin -safemode leave //强制退出安全模式
2、找出问题所在,进行修复。

注:有错误之处可以指出,一起交流,相互进步

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>