HDFS 教程(超详细)

1. HDFS 介绍

1.1 HDFS 背景及定义

  1. HDFS 产生背景

      随着数据量越来越大,在一个操作系统管辖的范围内存不下了,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理和维护,迫切需要一种系统来管理多台机器上的文件,这就是分布式文件管理系统。HDFS 只是分布式文件管理系统中的一种。

  2. HDFS 概念

      HDFS(Hadoop Distributed File System),它是一个文件系统,用于存储文件,通过目录树来定位文件;其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。
      HDFS 的设计适合一次写入,多次读出的场景,且不支持文件的修改。适合用来做数据分析,并不适合用来做网盘应用。

1.2 HDFS 的优缺点

  1. 优点

    (1)高容错性

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

    (2)适合大数据处理

      ① 数据规模:能够处理数据规模达到 GB、TB、甚至 PB 级别的数据。
      ② 文件规模:能够处理百万规模以上的文件数量,数量相当之大。

    (3)流式数据访问,它能保证数据的一致性。
    (4)可构建在廉价机器上,通过多副本机制,提高可靠性。

  2. 缺点

    (1)不适合低延时数据访问,比如毫秒级的存储数据,是做不到的。
    (2)无法高效的对大量小文件进行存储。

      ① 存储大量小文件的话,它会占用 NameNode 大量的内存来存储文件、目录和块信息。这样是不可取的,因为 NameNode 的内存总是有限的。
      ② 小文件存储的寻道时间会超过读取时间,它违反了 HDFS 的设计目标。

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

      ① 一个文件只能有一个写,不允许多个线程同时写。
      ② 仅支持数据 append(追加),不支持文件的随机修改。

1.3 HDFS 组成架构

在这里插入图片描述

  1. Client: 就是客户端。

    (1)文件切分。文件上传 HDFS 的时候,Client 将文件切分成一个一个的 Block,然后进行存储。
    (2)与 NameNode 交互,获取文件的位置信息。
    (3)与 DataNode 交互,读取或者写入数据。
    (4)Client 提供一些命令来管理 HDFS,比如启动或者关闭 HDFS。
    (5)Client 可以通过一些命令来访问 HDFS。

  2. NameNode: 就是 Master,它是一个主管、管理者。

    (1)管理 HDFS 的名称空间。
    (2)管理数据块(Block)映射信息。
    (3)配置副本策略。
    (4)处理客户端读写请求。

  3. DataNode: 就是 Slave,NameNode 下达命令,DataNode 执行实际的操作。

    (1)存储实际的数据块。
    (2)执行数据块的读/写操作。

  4. SecondaryNameNode: 并非 NameNode 的热备。当 NameNode 挂掉时,它并不能马上替换 NameNode 并提供服务。

    (1)辅助 NameNode,分担其工作量。
    (2)定期合并 Fsimage 和 Edits,并推送给 NameNode。
    (3)在紧急情况下,可辅助恢复 NameNode。

1.4 HDFS 文件块大小

  1. HDFS 中的文件在物理上是分块存储(block),块的大小可以通过配置参数(dfs.blocksize)来规定,默认大小在 hadoop2.x 版本中是128M,老版本中是 64M。

  2. HDFS 的块比磁盘的块大,其目的是为了最小化寻址开销。如果块设置得足够大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间。因而,传输 一个由多个块组成的文件的时间取决于磁盘传输速率。

  3. 如果寻址时间约为 10ms,而传输速率为 100MB/s,为了使寻址时间仅占传输时间的 1%,我们要将块大小设置约为 100MB。默认的块大小 128MB。
    块的大小:10ms*100*100M/s = 100M

    思考: 为什么块的大小不能设置太小,也不能设置太大?

    (1)HDFS 的块设置太小,会增加寻址时间,程序一直在找块的开始位置;
    (2)如果块的设置太大,从磁盘传输的时间会明显大于定位这个块开始位置所需的时间。导致程序在处理这块数据时,会非常慢。

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

2. HDFS 的 Shell 操作

  1. 基本语法

    hadoop fs 具体命令hdfs dfs 具体命令

  2. 命令大全
    在这里插入图片描述

  3. 常用命令

命令 说明 举例
-help 输出这个命令参数 hadoop fs -help rm
-ls 显示目录信息 hadoop fs -ls /
-mkdir 在 HDFS 上创建目录 hadoop fs -mkdir -p /usr/input
-rmdir 删除空目录 hadoop fs -rmdir /test
-rm 删除文件或文件夹 hadoop fs -rm /usr/input/test.txt
-moveFromLocal 从本地剪切粘贴到 HDFS hadoop fs -moveFromLocal a.txt /usr/input
-copyFromLocal 从本地文件系统中拷贝文件到 HDFS 路径去 hadoop fs -copyFromLocal c.txt /
-copyToLocal 从 HDFS 拷贝到本地 hadoop fs -copyToLocal /usr/input/a.txt
-appendToFile 追加一个文件到已经存在的文件末尾 hadoop fs -appendToFile b.txt /usr/input/a.txt
-cat 显示文件内容 hadoop fs -cat /usr/input/a.txt
-cp 从 HDFS 的一个路径拷贝到 HDFS 的另一个路径 hadoop fs -cp /usr/input/a.txt /f.txt
-mv 在 HDFS 目录中移动文件 hadoop fs -mv /f.txt /usr/input/
-get 等同于 copyToLocal hadoop fs -get /usr/input/a.txt
-put 等同于 copyFromLocal hadoop fs -put d.txt /usr/input/
-getmerge 合并下载多个文件 hadoop fs -getmerge /usr/input/* ./tmp.txt
-tail 显示一个文件的末尾 hadoop fs -tail /usr/input/a.txt
-chgrp 、-chmod、-chown Linux 文件系统中的用法一样,修改文件所属权限 hadoop fs -chmod 666 /usr/input/a.txt
hadoop fs -chown lyh:dell /usr/input/a.txt
-du 统计文件夹的大小信息 hadoop fs -du -s -h /usr/input
hadoop fs -du -h /usr/input
-setrep 设置 HDFS 中文件的副本数量 hadoop fs -setrep 10 /usr/input/a.txt

说明: 这里设置的副本数只是记录在 NameNode 的元数据中,是否真的会有这么多副本,还得看DataNode 的数量。因为目前只有 3 台设备,最多也就 3 个副本,只有节点数的增加到 10 台时,副本数才能达到 10。

3. HDFS 客户端操作

3.1 HDFS 客户端环境准备

  1. 将 hadoop 的安装包解压到本系统到非中文路径(E:\hadoop\hadoop-2.7.7)

  2. 配置 HADOOP_HOME 环境变量

    在这里插入图片描述
    在这里插入图片描述

  3. 创建一个 Maven 工程 hdfs,创建一个 module 为 HdfsClientDemo

  4. 导入相应的依赖坐标 + 日志添加

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.8.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>2.7.7</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>2.7.7</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>2.7.7</version>
    </dependency>
</dependencies>
  1. 在项目的 src/main/resources 目录下,新建一个文件,命名为 log4j.properties,在文件中填入
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
  1. 创建包 hdfs 并创建 HdfsClient 类
package hdfs;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

public class HDFSClient {

    public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException {

        // 1.获取hdfs客户端对象
        Configuration conf = new Configuration();
//        conf.set("fs.defaultFS", "hdfs://master:9000");
//        FileSystem fs = FileSystem.get(conf);
        FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), conf, "root");

        // 2.在hdfs上创建路径
        fs.mkdirs(new Path("/usr/lyh/tmp"));

        // 3.关闭资源
        fs.close();
    }
}
  1. 执行程序

    说明:上面代码中如果使用注释的代码,运行时需要配置用户名称
    在这里插入图片描述
    客户端去操作 HDFS 时,是有一个用户身份的。默认情况下,HDFS 客户端 API 会从 JVM 中获取一个参数来作为自己的用户身份:-DHADOOP_USER_NAME=root,root 为用户名称。

3.2 HDFS 的 API 操作

3.2.1 HDFS 文件上传、下载、删除、更名

  1. 将 hdfs-site.xml 拷贝到项目的根目录下
<configuration>
	<property>
		<name>dfs.replication</name>
        <value>1</value>
	</property>
</configuration>
  1. 编写源代码
@Test
public void testHDFSAPI() throws IOException, InterruptedException, URISyntaxException {
    // 获取文件系统
    Configuration configuration = new Configuration();
    configuration.set("dfs.replication", "2");
    FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");

    // 1 文件上传
    fs.copyFromLocalFile(new Path("f:/0/input/input.txt"), new Path("/input.txt"));

    // 2 文件下载
    // boolean delSrc 指是否将原文件删除
    // Path src 指要下载的文件路径
    // Path dst 指将文件下载到的路径
    // boolean useRawLocalFileSystem 是否开启文件校验
    fs.copyToLocalFile(false, new Path("/input.txt"), new Path("f:/0/output/input.txt"), true);

    // 3 文件夹删除
    fs.delete(new Path("/tmp/"), true);

    // 4 文件名更改
    fs.rename(new Path("/input.txt"), new Path("/input1.txt"));

    // 关闭资源
    fs.close();
}
  1. 参数优先级

    参数优先级排序:(1)客户端代码中设置的值 >(2)ClassPath下的用户自定义配置文件 >(3)然后是服务器的默认配置

3.2.2 HDFS 文件详情查看

/**
* 文件详情查看
* 查看文件名称、权限、长度、块信息
*/
@Test
public void testListFiles() throws IOException, InterruptedException, URISyntaxException {

	// 1获取文件系统
	Configuration configuration = new Configuration();
	FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");

	// 2 获取文件详情
	RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);
	
	while (listFiles.hasNext()) {
   		LocatedFileStatus status = listFiles.next();

    	// 输出详情
    	// 文件名称
   		System.out.println(status.getPath().getName());
    	// 长度
    	System.out.println(status.getLen());
    	// 权限
    	System.out.println(status.getPermission());
    	// 分组
    	System.out.println(status.getGroup());

    	// 获取存储的块信息
    	BlockLocation[] blockLocations = status.getBlockLocations();

    	for (BlockLocation blockLocation : blockLocations) {

        	// 获取块存储的主机节点
        	String[] hosts = blockLocation.getHosts();

        	for (String host : hosts) {
            	System.out.println(host);
        	}
    	}
    	System.out.println("---------------------");
	}

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

3.2.3 HDFS 文件和文件夹判断

/**
* 文件和文件夹判断
 */
@Test
public void testListStatus() throws IOException, InterruptedException, URISyntaxException {

    // 1 获取文件配置信息
    Configuration configuration = new Configuration();
    FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");

    // 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();
}

3.3 HDFS的 I/O 流操作

  上面的 API 操作 HDFS 系统都是框架封装好的。我们可以采用 IO 流的方式实现数据的上传和下载来实现上述 API 的操作。

3.3.1 HDFS 文件上传

@Test
public void putFileToHDFS() throws IOException, InterruptedException, URISyntaxException {

    // 1 获取文件系统
    Configuration configuration = new Configuration();
    FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");

    // 2 创建输入流
    FileInputStream fis = new FileInputStream(new File("f:/0/input/input.txt"));

    // 3 获取输出流
    FSDataOutputStream fos = fs.create(new Path("/input.txt"));

    // 4 流对拷
    IOUtils.copyBytes(fis, fos, configuration);

    // 5 关闭资源
    IOUtils.closeStream(fos);
    IOUtils.closeStream(fis);
    fs.close();
}

3.3.2 HDFS 文件下载

@Test
public void getFileFromHDFS() throws IOException, InterruptedException, URISyntaxException{

    // 1 获取文件系统
    Configuration configuration = new Configuration();
    FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");

    // 2 获取输入流
    FSDataInputStream fis = fs.open(new Path("/input.txt"));

    // 3 获取输出流
    FileOutputStream fos = new FileOutputStream(new File("f:/0/output/output.txt"));

    // 4 流的对拷
    IOUtils.copyBytes(fis, fos, configuration);

    // 5 关闭资源
    IOUtils.closeStream(fos);
    IOUtils.closeStream(fis);
    fs.close();
}

3.3.3 定位文件读取

  1. 下载第一块
@Test
public void readFileSeek1() throws IOException, InterruptedException, URISyntaxException{

	// 1 获取文件系统
	Configuration configuration = new Configuration();
	FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "atguigu");
		
	// 2 获取输入流
	FSDataInputStream fis = fs.open(new Path("/hadoop-2.7.7.tar.gz"));
		
	// 3 创建输出流
	FileOutputStream fos = new FileOutputStream(new File("f:/hadoop-2.7.2.tar.gz.part1"));
		
	// 4 流的拷贝
	byte[] buf = new byte[1024];
		
	for(int i =0 ; i < 1024 * 128; i++){
		fis.read(buf);
		fos.write(buf);
	}
		
	// 5关闭资源
	IOUtils.closeStream(fis);
	IOUtils.closeStream(fos);
	fs.close();
}

  1. 下载第二块
@Test
public void readFileSeek2() throws IOException, InterruptedException, URISyntaxException{

	// 1 获取文件系统
	Configuration configuration = new Configuration();
	FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");
		
	// 2 打开输入流
	FSDataInputStream fis = fs.open(new Path("/hadoop-2.7.7.tar.gz"));
		
	// 3 定位输入数据位置
	fis.seek(1024*1024*128);
		
	// 4 创建输出流
	FileOutputStream fos = new FileOutputStream(new File("f:/hadoop-2.7.7.tar.gz.part2"));
		
	// 5 流的对拷
	IOUtils.copyBytes(fis, fos, configuration);
		
	// 6 关闭资源
	IOUtils.closeStream(fis);
	IOUtils.closeStream(fos);
}
  1. 合并文件

    在 Window 命令窗口中进入到目录 F:\,然后执行如下命令,对数据进行合并

type hadoop-2.7.7.tar.gz.part2 >> hadoop-2.7.7.tar.gz.part1

  合并完成后,将 hadoop-2.7.7.tar.gz.part1 重新命名为 hadoop-2.7.7.tar.gz。解压发现该 tar 包非常完整。

4. HDFS 的数据流

4.1 HDFS 写数据流程

4.1.1 写数据流程

在这里插入图片描述

  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. 客户端开始往 dn 1上传第一个 Block(先从磁盘读取数据放到一个本地内存缓存),以 Packet 为单位,dn1 收到一个 Packet 就会传给 dn2,dn2 传给 dn3;dn1 每传一个 packet 会放入一个应答队列等待应答。
  8. 当一个 Block 传输完成之后,客户端再次请求 NameNode上传第二个 Block 的服务器。(重复执行 3-7 步)。

4.1.2 网络拓扑-节点距离计算

在这里插入图片描述

4.1.3 机架感知(副本存储节点选择)

在这里插入图片描述
  对于常见情况,当复制因子为 3 时,HDFS 的放置策略是将一个副本放在本地机架中的一个节点上,将另一个副本放在本地机架中的不同节点上,将最后一个副本放在不同机架中的另一个节点上。

  此策略减少了机架间写入流量,这通常提高了写入性能。机架故障的可能性远低于节点故障的可能性;此策略不会影响数据可靠性和可用性保证。

4.2 HDFS 读数据流程

在这里插入图片描述

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

5. NameNode 和 SecondaryNameNode

5.1 NN 和 2NN 工作机制

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

在这里插入图片描述

  1. 第一阶段:NameNode启动

    (1)第一次启动 NameNode 格式化后,创建 Fsimage 和 Edits 文件。若不是第一次启动,直接加载 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。

5.2 Fsimage 和 Edits 解析

  1. 概念

    NameNode 被格式化之后,将在 /hadoop/hadoop-2.7.7/data/tmp/dfs/name/current 目录中产生如下文件

    Fsimage 文件(镜像文件):

      HDFS 文件系统元数据的一个永久性的检查点,其中包含 HDFS 文件系统的所有目录和文件 inode 的序列化信息。

    Edits 文件(编辑日志):

      存放 HDFS 文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到 edits 文件中。

    seen_txid

      文件保存的是一个数字,就是最后一个 edits_的数字

  2. oiv 查看 Fsimage 文件

    ① 基本语法

hdfs oiv -p 文件类型 -i 镜像文件 -o 转换后文件输出路径

  ② 实操

hdfs oiv -p XML -i fsimage_0000000000000000262 -o /hadoop/hadoop-2.7.7/fsimage.xml
  1. oev 查看 Edits 文件

    ① 基本语法

hdfs oev -p 文件类型 -i 编辑日志 -o 转换后文件输出路径

  ② 实操

hdfs oev -p XML -i edits_0000000000000000261-0000000000000000262 -o /hadoop/hadoop-2.7.7/edits.xml

5.3 CheckPoint 时间设置

  1. 通常情况下,SecondaryNameNode 每隔一小时执行一次

    hdfs-default.xml

<property>
  <name>dfs.namenode.checkpoint.period</name>
  <value>3600</value>
</property>
  1. 一分钟检查一次操作次数,当操作次数达到 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 >

5.4 NameNode 故障处理

方法一:将 SecondaryNameNode 中数据拷贝到 NameNode 存储数据的目录

  1. kill -9 NameNode 进程
  2. 删除 NameNode 存储的数据(/hadoop/hadoop-2.7.7/data/tmp/dfs/name)
rm -rf /hadoop/hadoop-2.7.7/data/tmp/dfs/name/*
  1. 拷贝 SecondaryNameNode 中数据到原 NameNode 存储数据目录
scp -r slave2:/hadoop/hadoop-2.7.7/data/tmp/dfs/namesecondary/* ./name/
  1. 重新启动 NameNode
hadoop-daemon.sh start namenode

方法二:使用 -importCheckpoint 选项启动 NameNode 守护进程,从而将 SecondaryNameNode 中数据拷贝到 NameNode 目录中。

  1. 修改 hdfs-site.xml
<property>
  <name>dfs.namenode.checkpoint.period</name>
  <value>120</value>
</property>

<property>
  <name>dfs.namenode.name.dir</name>
  <value>/hadoop/hadoop-2.7.7/data/tmp/dfs/name</value>
</property>
  1. kill -9 NameNode 进程

  2. 删除 NameNode 存储的数据(/hadoop/hadoop-2.7.7/data/tmp/dfs/name)

rm -rf /hadoop/hadoop-2.7.7/data/tmp/dfs/name/*
  1. 如果 SecondaryNameNode 不和 NameNode 在一个主机节点上,需要将 SecondaryNameNode 存储数据的目录拷贝到 NameNode 存储数据的平级目录(/hadoop/hadoop-2.7.7/data/tmp/dfs),并删除 in_use.lock (namesecondary 目录下)文件
scp -r slave2:/hadoop/hadoop-2.7.7/data/tmp/dfs/namesecondary ./
rm -rf in_use.lock
  1. 导入检查点数据(等待一会ctrl+c结束掉)
hdfs namenode -importCheckpoint
  1. 启动 NameNode
hadoop-daemon.sh start namenode

5.5 集群安全模式

  1. 概述

  ① NameNode 启动时,首先将映像文件(fsimage)载入内存,并执行编辑日志(edits)中的各项操作。一旦在内存中成功建立文件系统元数据的映像,则创建一个新的镜像文件和一个空的编辑日志。此时,NameNode 开始监听 DataNode 请求。但是此刻,NameNode 运行在安全模式,即 NameNode 的文件系统对于客户端来说是只读的。

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

  ③ 如果满足“最小复本条件”,NameNode 会在 30 秒钟之后就退出安全模式。所谓的最小复本条件指的是在整个文件系统中 99.9% 的块满足最小复本级别(默认值:dfs.replication.min=1)。在启动一个刚刚格式化的 HDFS 集群时,因为系统中还没有任何块,所以 NameNode 不会进入安全模式。

  1. 基本语法

    集群处于安全模式,不能执行重要操作(写操作)。集群启动完成后,自动退出安全模式。

    ① 查看安全模式状态

hdfs dfsadmin -safemode get

  ② 进入安全模式状态

hdfs dfsadmin -safemode enter

  ③ 离开安全模式状态

hdfs dfsadmin -safemode leave

  ④ 等待安全模式状态

hdfs dfsadmin -safemode wait

5.6 NameNode 多目录配置

  NameNode 的本地目录可以配置成多个,且每个目录存放内容相同,增加了可靠性

  1. 停止集群,删除 data 和 logs 中所有数据。
  2. 在 hdfs-site.xml 文件中增加如下内容
<property>
    <name>dfs.namenode.name.dir</name>
	<value>file:///${hadoop.tmp.dir}/dfs/name1,file:///${hadoop.tmp.dir}/dfs/name2</value>
</property>
  1. 格式化集群并启动
hdfs namenode -format
start-dfs.sh

6. DataNode

6.1 DataNode 工作机制

在这里插入图片描述

  1. 一个数据块在 DataNode 上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
  2. DataNode 启动后向 NameNode 注册,通过后,周期性(1小时)的向 NameNode 上报所有的块信息。
  3. 心跳是每 3 秒一次,心跳返回结果带有 NameNode 给该 DataNode 的命令如复制块数据到另一台机器,或删除某个数据块。如果超过 10 分钟没有收到某个 DataNode 的心跳,则认为该节点不可用。
  4. 集群运行中可以安全加入和退出一些机器。

6.2 数据完整性

  如下是 DataNode 节点保证数据完整性的方法

  1. 当 DataNode 读取 Block 的时候,它会计算 CheckSum。
  2. 如果计算后的 CheckSum,与 Block 创建时值不一样,说明 Block 已经损坏。
  3. Client 读取其他 DataNode 上的 Block。
  4. DataNode 在其文件创建后周期验证 CheckSum,如下图所示。

在这里插入图片描述

6.3 掉线时限参数设置

在这里插入图片描述
  需要注意的是 hdfs-site.xml 配置文件中的 heartbeat.recheck.interval 的单位为毫秒,dfs.heartbeat.interval 的单位为秒。

<property>
    <name>dfs.namenode.heartbeat.recheck-interval</name>
    <value>300000</value>
</property>
<property>
    <name>dfs.heartbeat.interval</name>
    <value>3</value>
</property>

6.4 服役新数据节点

  1. 环境准备

    ① 在 slave2 主机上再克隆一台 slave3 主机
    ② 修改 IP 地址和主机名称
    ③ 删除原来 HDFS 文件系统留存的文件(/hadoop/hadoop-2.7.7/data 和 log)

  2. 服役新节点具体步骤

    ① 修改集群所有节点(master 和所有 slave)的 ${HADOOP_HOME}/conf/slaves 文件,增加新节点 ip 或 hostname:

master
slave1
slave2
slave3

  ② 集群所有节点上修改系统的/ etc/hosts 配置文件,增加 ip 与 hostname 的映射

192.168.217.133 slave3

  ③ 启动DataNode,即可关联到集群

hadoop-daemon.sh start datanode
yarn-daemon.sh start nodemanager

在这里插入图片描述

6.5 退役旧数据节点

6.5.1 添加白名单

  添加到白名单的主机节点,都允许访问 NameNode,不在白名单的主机节点,都会被退出。

  1. 在 NameNode 的 /hadoop/hadoop-2.7.7/etc/hadoop 目录下创建 dfs.hosts 文件

    添加如下主机名称(不添加 slave3)

master
slave1
slave2
  1. 在 NameNode 的 hdfs-site.xml 配置文件中增加 dfs.hosts 属性
<property>
	<name>dfs.hosts</name>
	<value>/hadoop/hadoop-2.7.7/etc/hadoop/dfs.hosts</value>
</property>
  1. 配置文件分发
xsync hdfs-site.xml
  1. 刷新 NameNode、刷新 ResourceManager
hdfs dfsadmin -refreshNodes
yarn rmadmin -refreshNodes
  1. 在 web 浏览器上查看

6.5.2 黑名单退役

  在黑名单上面的主机都会被强制退出。

  1. 在 NameNode 的 /hadoop/hadoop-2.7.7/etc/hadoop 目录下创建 dfs.hosts.exclude 文件

    添加如下主机名称(要退役的节点)

slave3
  1. 在 NameNode 的 hdfs-site.xml 配置文件中增加 dfs.hosts.exclude 属性
<property>
	<name>dfs.hosts.exclude</name>
 	<value>/hadoop/hadoop-2.7.7/etc/hadoop/dfs.hosts.exclude</value>
</property>
  1. 刷新 NameNode、刷新 ResourceManager
hdfs dfsadmin -refreshNodes
yarn rmadmin -refreshNodes
  1. 检查 Web 浏览器,退役节点的状态为 decommission in progress(退役中),说明数据节点正在复制块到其他节点
  2. 等待退役节点状态为 decommissioned(所有块已经复制完成),停止该节点及节点资源管理器。注意:如果副本数是 3,服役的节点小于等于 3,是不能退役成功的,需要修改副本数后才能退役

6.6 Datanode 多目录配置

  DataNode 也可以配置成多个目录,每个目录存储的数据不一样。即:数据不是副本。

  1. 停止集群,删除 data 和 logs 中所有数据。
  2. 在 hdfs-site.xml 文件中增加如下内容
<property>
 	<name>dfs.datanode.data.dir</name>
	<value>file:///${hadoop.tmp.dir}/dfs/data1,file:///${hadoop.tmp.dir}/dfs/data2</value>
</property>
  1. 格式化集群并启动
hdfs namenode -format
start-dfs.sh

7. HDFS 2.X 新特性

7.1 集群间数据拷贝

  采用 distcp 命令实现两个 Hadoop 集群之间的递归数据复制

hadoop distcp hdfs://hadoop1:9000/hello.txt hdfs://hadoop2:9000/hello.txt

7.2 小文件存档

  1. hdfs 存储小文件弊端

      每个文件均按块存储,每个块的元数据存储在 namenode 的内存中,因此 hadoop 存储小文件会非常低效。因为大量的小文件会耗尽 namenode 中的大部分内存。但注意,存储小文件所需要的磁盘容量和存储这些文件原始内容所需要的磁盘空间相比也不会增多。例如,一个 1 MB 的文件以大小为 128 MB 的块存储,使用的是 1 MB 的磁盘空间,而不是 128 MB。

  2. 解决存储小文件办法之一

      Hadoop 存档文件或 HAR 文件,是一个更高效的文件存档工具,它将文件存入 HDFS 块,在减少 namenode 内存使用的同时,允许对文件进行透明的访问。具体说来,Hadoop 存档文件对内还是一个一个独立文件,对 namenode 而言却是一个整体,减少了 namenode 的内存。
    在这里插入图片描述

  3. 案例实操

    ① 需要启动 yarn 进程

 start-yarn.sh

  ② 归档文件

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

hadoop archive -archiveName input.har -p /usr/input  /usr/output

  ③ 查看归档

hadoop fs -ls -R har:///usr/output/input.har

在这里插入图片描述

  ④ 解归档

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

7.3 回收站

  开启回收站功能,可以将删除的文件在不超时的情况下,恢复原数据,起到防止误删除、备份等作用。

  1. 回收站参数设置及工作机制

    ① 默认值 fs.trash.interval=0, 0 表示禁用回收站;其他值表示设置文件的存活时间。
    ② 默认值 fs.trash.checkpoint.interval=0,检查回收站的间隔时间。
    ③ 要求 fs.trash.checkpoint.interval <= fs.trash.interval。

    在这里插入图片描述

  2. 启用回收站

    ① 修改 core-site.xml,配置垃圾回收时间为 10 分钟
    ② 进入垃圾回收站用户名称,默认是 dr.who,修改为 root 用户

<!-- 回收站文件的存活时间 -->
<property>
  <name>fs.trash.interval</name>
  <value>10</value>
</property>

<!-- 修改垃圾回收站用户名称 -->
<property>
  <name>hadoop.http.staticuser.user</name>
  <value>root</value>
</property>
  1. 示例:删除 /input.txt
hadoop fs -rm /input.txt

在这里插入图片描述

  1. 查看回收站

    回收站在集群中的路径:/user/root/.Trash/….
    在这里插入图片描述

  2. 恢复回收站数据

hadoop fs -mv /user/root/.Trash/Current/input.txt /
  1. 清空回收站
hadoop fs -expunge
  1. 通过程序删除的文件不会经过回收站,需要调用 moveToTrash() 才进入回收站
Trash trash = New Trash(conf);
trash.moveToTrash(path);

7.4 快照管理

  快照相当于对目录做一个备份。并不会立即复制所有文件,而是记录文件变化。

命令 说明
hdfs dfsadmin -allowSnapshot 路径 开启指定目录的快照功能
hdfs dfsadmin -disallowSnapshot 路径 禁用指定目录的快照功能,默认禁用
hdfs dfs -createSnapshot 路径 对目录创建快照
hdfs dfs -createSnapshot 路径 名称 指定名称创建快照
hdfs dfs -renameSnapshot 路径 旧名称 新名称 重命名快照
hdfs lsSnapshottableDir 列出当前用户所有可快照目录
hdfs snapshotDiff 路径1 路径2 比较两个快照目录的不同之处
hdfs dfs -deleteSnapshot 路径 名称 删除快照

猜你喜欢

转载自blog.csdn.net/weixin_42837961/article/details/105612928