Hadoop之hdfs

HDFS

HDFS概述

Hadoop Distributed File System

一种统一管理多个节点上的文件的分布式系统。

使用场景:适合一次写入,多次读出的场景,且不支持文件修改。适合用来做数据分析,并不适合做网盘应用。

HDFS优点

1)高容错性

数据自动保存多个副本。它通过增加副本的形式,提高容错性。某一个副本丢失后,可以自动恢复。

2)适合处理大数据

1、数据规模:能够处理GB、TB、甚至PB级别的数据。

2、文件规模:能够处理百万规模以上的文件数量。

3)可构建在廉价机器上,通过多副本机制,提高可靠性。

HDFS缺点

1)不适合低延时数据访问,比如毫秒级的存储数据。

2)无法高效的对大量小文件进行存储。

1、存储大量小文件的话,会占用NameNode大量内存来储存文件目录和快信息。

2、小文件存储的寻址时间会超过读取时间,它违反了HDFS的设计目标。

3)不支持并发写入、文件随机修改

1、一个文件只能有一个写,不允许多个线程同时写。

2、仅支持数据append(追加),不支持文件的随机修改。

 

HDFS组成架构

 

HDFS文件块大小**

为什么块大小不能设置太小,也不能设置太大?

1、HDFS块大小设置太小,会增加寻址时间,程序一直在找块的开始位置。

2、如果块设置的太大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间。导致程序在处理这块数据时,会非常慢。

总结:HDFS的块大小设置主要取决于磁盘传输速率。

(可通过修改配置hdfs-site.xml参数dfs.blocksize(默认值134217728即128m)来修改块大小)

HDFS常用命令(linux)

基本语法

#两个命令相同
bin/hadoop fs xxx
bin/hdfs dfs xxx

命令大全

#可通过hadoop fs -help xx查看对应命令详情
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 ...]]

常用命令

上传
#从本地剪切粘贴到HDFS
-moveFromLocal <localsrc> ... <dst>
#从本地拷贝粘贴到HDFS
-copyFromLocal <localsrc> ... <dst>
#追加一个文件到已经存在的文件末尾
-appendToFile <localsrc> ... <dst>
#等同于copyFromLocal
-put <localsrc> ... <dst>
下载
#从HDFS拷贝到本地
-copyToLocal <src> ... <localdst>
#等同于copyToLocal
-get <src> ... <localdst>
#合并下载多个文件(注意该命令全小写)
-getmerge <src> ... <localdst>
其他文件操作
#等同于liunx
-ls
-mkdir
-cat
-chgrp、-chmod、-chown
-cp
-mv
-tail
-rm
-rmdir
#统计文件夹大小
-du [-s 合并显示总大小] [-h 格式化显示大小单位] [-v] [-x] <path> ...
#设置HDFS中文件的副本数量(若文件副本数量大于节点数量,会在新增加节点时自动增加副本)
-setrep <rep 数量> <path 文件路径> ...

 

HDFS客户端操作(Windows)**

HDFS客户端环境准备

#环境变量
依赖文件:hadoop-3.1.0
配置HADOOP_HOME环境变量
#若有问题可将hadoop-3.1.0的bin目录下的hadoop.dll和winutils.exe放到C:/windows/system32目录下。

HDFS配置MAVEN工程依赖

创建一个Maven工程并添加依赖坐标(pom.xml)

<dependencies>
   <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.12</version>
   </dependency>
   <dependency>
       <groupId>org.apache.logging.log4j</groupId>
       <artifactId>log4j-slf4j-impl</artifactId>
       <version>2.12.0</version>
   </dependency>
   <dependency>
       <groupId>org.apache.hadoop</groupId>
       <artifactId>hadoop-client</artifactId>
       <version>3.1.3</version>
   </dependency>
</dependencies>

在src/main/resources目录下新建文件log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error" strict="true" name="XMLConfig">
   <Appenders>
       <!-- 类型名为Console,名称为必须属性 -->
       <Appender type="Console" name="STDOUT">
           <!-- 布局为PatternLayout的方式,
           输出样式为[INFO] [2018-01-22 17:34:01][org.test.Console]I'm here -->
           <Layout type="PatternLayout"
                   pattern="[%p] [%d{yyyy-MM-dd HH:mm:ss}][%c{10}]%m%n" />
       </Appender>
   </Appenders>
   <Loggers>
       <!-- 可加性为false -->
       <Logger name="test" level="info" additivity="false">
           <AppenderRef ref="STDOUT" />
       </Logger>
       <!-- root loggerConfig设置 -->
       <Root level="info">
           <AppenderRef ref="STDOUT" />
       </Root>
   </Loggers>
</Configuration>

HDFS的API操作

创建文件系统对象
// 方式一:
/*
uri : HDFS的地址(NameNode的地址)
   conf : 配置内容
   user : 操作集群的用户
*/
FileSystem fs = FileSystem.get(final URI uri, final Configuration conf,final String user)
 
// 方式二:
/*
1.需要在Configuration中配置HDFS的路径
   2.先运行一下该类(否则找不到)
   选中对应类的配置 ----> ditConfigurations ----> VM Options ---->填入-DHADOOP_USER_NAME=atguigu
*/
Configuration conf = new Configuration();
//需要配置HDFS的地址
conf.set("fs.defaultFS","hdfs://hadoop102:9820");
FileSystem fs = FileSystem.get(conf);

// 拓展:main(String[] args)方法参数由控制台传递,也可在idea中edit configurations中的program arguments中设置,参数由空格隔开。
// 控制台: java xxx.class(字节码文件) arg1 arg2 ...
// idea:program arguments[ arg1 arg2 ...]
常用文件系统操作

上传

/*
delSrc : 是否删除源文件(本地)
   overwrite : 如果目标文件已经存在是否覆盖目标文件(HDFS)如果为false : 如果目标文件存在则报错
   src : 源文件路径(本地)
   dst : 目标文件路径(HDFS)
*/
fs.copyFromLocalFile((boolean delSrc, boolean overwrite,Path src, Path dst)

下载

/*
delSrc : 是否删除源文件(HDFS)
    src :源文件路径(HDFS)
    dst : 目标文件路径(本地)
    useRawLocalFileSystem :是否使用RawLocalFileSystem
*/
fs.copyToLocalFile(boolean delSrc, Path src, Path dst,boolean useRawLocalFileSystem)

删除文件夹

/*
f : 要删除的文件和目录的路径
recursive : 是否递归删除
注意:如果是删除目录的话该值只能为true否则报错
如果是文件(空目录)该值既可以为true也可以为false
*/
fs.delete(Path f, boolean recursive)

修改文件名

/*
src : 源文件路径(HDFS)
dst : 目标文件路径(HDFS)
*/
fs.rename(Path src, Path dst)

// 修改文件名
fs.rename(new Path("/aaa.txt"),new Path("/ccc.txt"));

// 移动文件
fs.rename(new Path("/ccc.txt"),new Path("/output"));

查看文件详情

/*
    f : 是文件或目录的路径
    recursive :是否递归
*/
RemoteIterator<LocatedFileStatus> remoteIterator = fs.listFiles(final Path f, final boolean recursive)
// 遍历
while(remoteIterator.hasNext()){
       //获取文件
       LocatedFileStatus fileStatus = remoteIterator.next();
       System.out.println("====================" + fileStatus.getPath().getName()+ "=====");
       System.out.println("文件名:" + fileStatus.getPath().getName());
       System.out.println("副本数" + fileStatus.getReplication());

       //获取块信息
       BlockLocation[] blockLocations = fileStatus.getBlockLocations();
       System.out.println(Arrays.toString(blockLocations));
}

查看文件的类型

 /*
f : 目标文件或目录的路径
*/

FileStatus[] status = this.fs.listStatus(Path f);
       //遍历
       for (FileStatus f : status) {
           if (f.isFile()){
               System.out.println(f.getPath().getName() + "是一个文件");
          }else if (f.isDirectory()){
               System.out.println(f.getPath().getName() + "是一个目录");
          }
      }

使用流上传或下载文件

上传

//输入流 - 文件输入流
FileInputStream fis = new FileInputStream("D:\io\hdfs\aa.txt");
//输出流 - 向HDFS上输出的流
FSDataOutputStream fos = fs.create(new Path("/aa.txt"));
//一边读一边写
/*
copyBytes(InputStream in, OutputStream out,int buffSize, boolean close)
in :输入流
out :输出流
buffsize :缓冲区大小
close : 是否关闭流
*/
IOUtils.copyBytes(fis,fos,1024,false);
//关流
IOUtils.closeStream(fis);
IOUtils.closeStream(fos);

下载

//输入流 - 从HDFS上输入的流
FSDataInputStream fis = fs.open(new Path("/aa.txt"));
//输出流 - 文件输出流
FileOutputStream fos = new FileOutputStream("D:\io\hdfs\aa.txt");
//一边读一边写(文件对拷)
IOUtils.copyBytes(fis,fos,1024,true);

配置的优先级

配置的位置

  1. xxx.default.xml

  2. xxx.site.xml

  3. maven工程src/main/resources中xxx.site.xml

  4. 代码中Configuration做配置

配置的优先级

4 > 3 > 2 > 1

 

HDFS读写数据流程**

HDFS写数据流程

  1. Client创建FileSystem对象,向NameNode请求上传文件,NameNode检查文件是否存在,文件目录是否存在,判断用户是否有权限

  2. NameNode响应可以上传

  3. Client切分文件块,并向NameNode上传第一个Block,请求NameNode返回DataNode节点

  4. NameNode返回dfs.replication数量的节点

  5. Client创建输出流,向dn1请求建立Block传输通道,成功后继续向dn2请求,成功后向dn3请求

  6. dn3,dn2,dn1返回成功应答

  7. Client向dn1以数据包Package的形式上传数据,dn1往内存中Bytebuffer写入的同时,写到dn1本地磁盘,同时内存中Bytebuffer向dn2的Bytebuffer中上传...

    // Package传输时放入应答队列等待应答

    // 若传输途中有dn节点宕机,则会跳过此节点,传输完成后,NameNode重新制定dn存储副本,该节点重启后,删除未完成的传输内容

节点距离计算与机架感知

机架感知官方说明地址:http://hadoop.apache.org/docs/r3.1.3/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html#Data_Replication

2.x版本:在同一机架上选择第二个节点

3.x版本:在不同机架上选择第二个节点,并在考虑到效率问题,在第二个节点所在机架上选择第三个节点,若节点数超过3个,则完全随机。

HDFS读数据流程

  1. Client创建FileSystem对象,向NameNode请求下载文件,NameNode判断是否有该文件,判断该用户是否有权限

  2. NameNode返回目标文件的元数据

  3. Client创建流,挑选一台DataNode(就近原则,然后随机)逐个找DataNode读取数据块

  4. DataNode从磁盘读取数据输入流,以Packet为单位做校验

  5. Client以Packet为单位接收数据,先在本地缓存,然后写入文件

 

NameNode和SecondaryNameNode**

NN和2NN工作机制(CheckPoint)

  1. Client发出请求

  2. Namenode收到请求后,将命令记录到滚动日志edits_inprogress_xx,并在内存中执行该命令

    // NameNode启动时,会将edits_inprogress_xx和fsimage读到内存

  3. 2NN不停向NN询问是否需要CheckPoint(配置在hdfs.default.xml中)

    // dfs.namenode.checkpoint.check.period 默认为60s

    当CheckPoint条件触发(配置在hdfs.default.xml中)

    // dfs.namenode.checkpoint.period 默认为3600s

    // dfs.namenode.checkpoint.txns 默认为1000000

  4. NameNode中将edits_inprogress_xx文件重命名为edits_xxx并且新建一个edits_inprogress_xx2文件继续存放滚动日志

  5. 将重命名后的edits_xxx文件及fsimage文件从磁盘拷贝到2NN,2NN会在内存中执行edits_xxx生成元数据并与fsimage中的元数据合并,生成文件fsimage_chkpoint

  6. 将2NN中fsimage_chkpoint拷贝到NN中,并重命名为fsimage,至此一个CheckPoint完成

拓展:

若NameNode故障,可紧急将2NN的数据(/opt/module/hadoop-3.1.3/data/tmp/dfs/name)拷贝到NameNode对应目录中,重启NameNode即可。

集群安全模式

NameNode启动

NameNode启动时,将Fsimage文件加载进内存,并执行Edits_inprogress_xx中的操作,在内存中建立元数据映像成功后,将创建新的Fsimage和一个空的滚动日志编辑文件,然后开始监听DataNode请求,过程中,NameNode一直处于安全模式,NameNode的文件对客户端说是只读的。

DataNode启动

HDFS系统中数据块的位置信息并不是由NameNode维护的,而是以块列表的形式存在DataNode中,在系统正常操作期间,NameNode会在内存中保留所有的块位置的信息。在安全模式下,各个DataNode会向NameNode发送最新的块列表信息,NameNode了解到足够多的快信息后,即可高效运行文件系统。

安全模式退出判断

最小副本条件:hdfs.default.xml 中

dfs.namenode.replication.min 默认值为1

如果满足最小副本条件,NameNode会在30秒之后退出安全模式。最小副本条件是指99.9%的块满足最小副本级别。在启动一个刚刚格式化的HDFS集群时,因为系统中还没有任何块,所以NameNode不会进入安全模式。

#安全模式命令
#查看安全模式状态
bin/hdfs dfsadmin -safemode get
#进入安全模式
bin/hdfs dfsadmin -safemode enter
#离开安全模式
bin/hdfs dfsadmin -safemode leave
#等待安全模式结束
bin/hdfs dfsadmin -safemode wait

DataNode工作机制**

DataNode启动后,向NameNode注册,报告该节点所存放的块信息,并且每周期(1小时)上报一次所有块信息。

DN心跳机制

心跳间隔:hdfs-default.xml

dfs.heartbeat.interval 默认为3s

Timeout计算公式:

Timeout=2*dfs.namenode.heartbeat.recheck-interval+10*dfs.heartbeat.interval

dfs.namenode.heartbeat.recheck-interval 默认为300000ms(5分钟)

dfs.heartbeat.interval 默认为3s

DataNode每3秒向NameNode发送心跳以证明该节点存活,心跳返回的结果带有NameNode给DataNode的命令,若NameNode超过10分钟30秒没收到某节点的心跳,则认为该节点不可用。

DN数据完整性

  1. DataNode读取Block的时候,会计算CheckSum

  2. 如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏

  3. Client读取其他DataNode上的Block

  4. DataNode在其文件创建后周期验证CheckSum

数据传递时是以Package包形式,包中携带chunk(由512字节+4字节(32位))作为检验和

服役新数据节点

#配置hostname
sudo vim /etc/hostname
#配置hosts
sudo vim /etc/hosts
#配置hadoop配置文件(使用同步脚本)
#直接启动DataNode即可关联到集群
hdfs --daemon start datanode
#如果数据不均衡,可以使用命令实现集群的再平衡
$HADOOP_HOME/sbin/start-balancer.sh

#补充:$HADOOP_HOME/etc/hadoop/workers中配置的是$HADOOP_HOME/sbin/start-dfs.sh脚本使用的集群配置,单独添加一台DN节点,只需配置好NameNode节点信息,就可加入集群

#注意:修改hostname需重启linux

白名单与黑名单

白名单

  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>
  1. 文件添加白名单节点(末行不得有空行)

hadoop102
hadoop103
hadoop104
  1. 分发配置

xsync hdfs-site.xml
  1. 刷新NameNode

hdfs dfsadmin -refreshNodes
  1. 更新ResourceManager节点

yarn rmadmin -refreshNodes
  1. 如果数据不均衡,可以用命令实现集群的再平衡

$HADOOP_HOME/sbin/start-balancer.sh

注意:直接移除白名单并刷新节点,会导致被移除的节点DataNode进程直接关闭,无法实行退役操作,即该节点数据无法迁移到别的节点。

 

黑名单

  1. 配置NameNode的hdfs-site.xml配置文件

<!-- 增加dfs.hosts.exclude属性 在目录创建对应文件 -->
<property>
  <name>dfs.hosts.exclude</name>
  <value>/opt/module/hadoop-3.1.3/etc/hadoop/dfs.hosts.exclude</value>
</property>
  1. 文件添加黑名单节点(末行不得有空行)

hadoop105
  1. 刷新NameNode

hdfs dfsadmin -refreshNodes
  1. 更新ResourceManager节点

yarn rmadmin -refreshNodes
  1. 检查web(hadoop102:9870),该节点状态为decommission in progress(退役中),说明该阶段数据块正复制到其他节点。

  2. 等待节点状态为decommissioned,表示已经复制完,停止该节点资源管理器。

  3. 如果数据不均衡,可以用命令实现集群的再平衡

$HADOOP_HOME/sbin/start-balancer.sh      

注意:如果退役会导致服役副本节点数小于配置副本数,则不能退役成功,需修改副本数。

DataNode多目录配置

DataNode也可以配置成多个目录存储数据,每个目录存储的数据不一样。即:数据不是副本。使用场景为增加硬盘时,扩容。

配置hdfs-site.xml

<!-- 配置后根据情况是否分发,重启该DN节点,会自动创建文件夹 -->
<property>
   <name>dfs.datanode.data.dir</name>
   <value>file:///${hadoop.tmp.dir}/dfs/data1,file:///${hadoop.tmp.dir}/dfs/data2</value>
</property>

 

原文地址:https://www.cnblogs.com/BookMiki/p/15003919.html