3 Hadoop-HDFS分布式⽂件系统

3.1 HDFS 简介

HDFS (全称:Hadoop Distribute File System,Hadoop 分布式⽂件系统)是 Hadoop 核⼼组成,是分布式存储服务。

分布式⽂件系统横跨多台计算机,在⼤数据时代有着⼴泛的应⽤前景,它们为存储和处理超⼤规模数据提供所需的扩展能⼒。

HDFS是分布式⽂件系统中的⼀种。

重要概念

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.2 HDFS 架构

在这里插入图片描述

  • NameNode(nn):Hdfs集群的管理者,Master
    • 维护管理Hdfs的名称空间(NameSpace)
    • 维护副本策略
    • 记录⽂件块(Block)的映射信息
    • 负责处理客户端读写请求
  • DataNode:NameNode下达命令,DataNode执⾏实际操作,Slave节点。
    • 保存实际的数据块
    • 负责数据块的读写
  • Client:客户端
    • 上传⽂件到HDFS的时候,Client负责将⽂件切分成Block,然后进⾏上传
    • 请求NameNode交互,获取⽂件的位置信息
    • 读取或写⼊⽂件,与DataNode交互
    • Client可以使⽤⼀些命令来管理HDFS或者访问HDFS

3.3 HDFS 客户端操作

  1. 基本语法
hadoop fs 具体命令 
hdfs dfs 具体命令
  1. 命令⼤全
[root@linux121 hadoop-2.9.2]# bin/hdfs dfs
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] <localsrc> ... <dst>]
 [-copyToLocal [-f] [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
 [-count [-q] [-h] [-v] [-t [<storage type>]] [-u] [-x] <path> ...]
 [-cp [-f] [-p | -p[topax]] [-d] <src> ... <dst>]
 [-createSnapshot <snapshotDir> [<snapshotName>]]
 [-deleteSnapshot <snapshotDir> <snapshotName>]
 [-df [-h] [<path> ...]]
 [-du [-s] [-h] [-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>]
 [-help [cmd ...]]
 [-ls [-C] [-d] [-h] [-q] [-R] [-t] [-S] [-r] [-u] [<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] <file>]
 [-test -[defsz] <path>]
 [-text [-ignoreCrc] <src> ...]
 [-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

3.HDFS命令演示

  1. 启动Hadoop集群(⽅便后续的测试)
[root@linux121 hadoop-2.9.2]$ sbin/start-dfs.sh
[root@linux122 hadoop-2.9.2]$ sbin/start-yarn.sh
  1. -help:输出这个命令参数
[root@linux121 hadoop-2.9.2]$ hadoop fs -help rm
  1. -ls: 显示⽬录信息
[root@linux121 hadoop-2.9.2]$ hadoop fs -ls /
  1. -mkdir:在HDFS上创建⽬录
[root@linux121 hadoop-2.9.2]$ hadoop fs -mkdir -p /lagou/bigdata
  1. -moveFromLocal:从本地剪切粘贴到HDFS
[root@linux121 hadoop-2.9.2]$ touch hadoop.txt
[root@linux121 hadoop-2.9.2]$ hadoop fs -moveFromLocal ./hadoop.txt
/lagou/bigdata
  1. -appendToFile:追加⼀个⽂件到已经存在的⽂件末尾
[root@linux121 hadoop-2.9.2]$ touch hdfs.txt
[root@linux121 hadoop-2.9.2]$ vi hdfs.txt

输⼊

namenode datanode block replication
[root@linux121 hadoop-2.9.2]$ hadoop fs -appendToFile hdfs.txt
/lagou/bigdata/hadoop.txt
  1. -cat:显示⽂件内容
[root@linux121 hadoop-2.9.2]$ hadoop fs -cat /lagou/bigdata/hadoop.txt
  1. -chgrp 、-chmod、-chown:Linux⽂件系统中的⽤法⼀样,修改⽂件所属权限
[root@linux121 hadoop-2.9.2]$ hadoop fs -chmod 666
/lagou/bigdata/hadoop.txt
[root@linux121 hadoop-2.9.2]$ hadoop fs -chown root:root
/lagou/bigdata/hadoop.txt
  1. -copyFromLocal:从本地⽂件系统中拷⻉⽂件到HDFS路径去
[root@linux121 hadoop-2.9.2]$ hadoop fs -copyFromLocal README.txt /
  1. -copyToLocal:从HDFS拷⻉到本地
[root@linux121 hadoop-2.9.2]$ hadoop fs -copyToLocal /lagou/bigdata/hadoop.txt ./
  1. -cp :从HDFS的⼀个路径拷⻉到HDFS的另⼀个路径
[root@linux121 hadoop-2.9.2]$ hadoop fs -cp /lagou/bigdata/hadoop.txt /hdfs.txt
  1. -mv:在HDFS⽬录中移动⽂件
[root@linux121 hadoop-2.9.2]$ hadoop fs -mv /hdfs.txt /lagou/bigdata/
  1. -get:等同于copyToLocal,就是从HDFS下载⽂件到本地
[root@linux121 hadoop-2.9.2]$ hadoop fs -get /lagou/bigdata/hadoop.txt ./
  1. -put:等同于copyFromLocal
[root@linux121 hadoop-2.9.2]$ hadoop fs -mkdir -p /user/root/test/
#本地⽂件系统创建yarn.txt
[root@linux121 hadoop-2.9.2]$ vim yarn.txt
resourcemanager nodemanager
[root@linux121 hadoop-2.9.2]$ hadoop fs -put ./yarn.txt /user/root/test/
  1. -tail:显示⼀个⽂件的末尾
[root@linux121 hadoop-2.9.2]$ hadoop fs -tail /user/root/test/yarn.txt
  1. -rm:删除⽂件或⽂件夹
[root@linux121 hadoop-2.9.2]$ hadoop fs -rm /user/root/test/yarn.txt
  1. -rmdir:删除空⽬录
[root@linux121 hadoop-2.9.2]$ hadoop fs -mkdir /test
[root@linux121 hadoop-2.9.2]$ hadoop fs -rmdir /test
  1. -du统计⽂件夹的⼤⼩信息
[root@linux121 hadoop-2.9.2]$ hadoop fs -du -s -h /user/root/test
[root@linux121 hadoop-2.9.2]$ hadoop fs -du -h /user/root/test
  1. -setrep:设置HDFS中⽂件的副本数量
[root@linux121 hadoop-2.9.2]$ hadoop fs -setrep 10 /lagou/bigdata/hadoop.txt

就算配置调到10台,机器只有3台,那么实际也只有3个副本
在这里插入图片描述

3.4 JAVA客户端

3.4.1 客户端环境准备

在这里插入图片描述

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

<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.9.2</version>
	 </dependency>
	 <!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoopclient -->
	 <dependency>
		 <groupId>org.apache.hadoop</groupId>
		 <artifactId>hadoop-client</artifactId>
		 <version>2.9.2</version>
	 </dependency>
	 <!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-hdfs
	-->
	 <dependency>
		 <groupId>org.apache.hadoop</groupId>
		 <artifactId>hadoop-hdfs</artifactId>
		 <version>2.9.2</version>
	 </dependency>
 </dependencies>

为了便于控制程序运⾏打印的⽇志数量,需要在项⽬的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

新建文件夹(mkdirs)

public class HdfsClient{
	@Test
	public void testMkdirs() throws IOException, InterruptedException,URISyntaxException {
		 // 1 获取⽂件系统
		Configuration configuration = new Configuration();
		 // 配置在集群上运⾏
		 // configuration.set("fs.defaultFS", "hdfs://linux121:9000");
		 // FileSystem fs = FileSystem.get(configuration);
		FileSystem fs = FileSystem.get(new URI("hdfs://linux121:9000"),
		configuration, "root");
		 // 2 创建⽬录
		fs.mkdirs(new Path("/test"));
		 // 3 关闭资源
		fs.close();
	}
}

遇到问题1:
如果不指定操作HDFS集群的⽤户信息,默认是获取当前操作系统的⽤户信息,出现权限被拒绝的问题,报错如下:

在这里插入图片描述

在这里插入图片描述

问题2:
windows解压安装Hadoop后,在调⽤相关API操作HDFS集群时可能会报错,这是由于Hadoop安装缺少windows操作系统相关⽂件所致,如下图:
在这里插入图片描述
从资料⽂件夹中找到winutils.exe拷⻉放到windows系统Hadoop安装⽬录的bin⽬录下即可!!

3.4.2 HDFS的API操作

1 上传文件

@Test
public void testCopyFromLocalFile() throws IOException,InterruptedException, URISyntaxException {
	 // 1 获取⽂件系统
	Configuration configuration = new Configuration();
	configuration.set("dfs.replication", "2");
	FileSystem fs = FileSystem.get(new URI("hdfs://linux121:9000"),configuration, "root");
	// 2 上传⽂件
	fs.copyFromLocalFile(new Path("e:/lagou.txt"), new Path("/lagou.txt"));
	 // 3 关闭资源
	fs.close();
	System.out.println("end");
}

也可以使用配置文件来设置

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

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

参数优先级
参数优先级排序:(1)代码中设置的值 >(2)⽤户⾃定义配置⽂件 >(3)服务器的默认配置

2 下载文件

@Test
public void testCopyToLocalFile() throws IOException, InterruptedException,URISyntaxException{
	 // 1 获取⽂件系统
	Configuration configuration = new Configuration();
	FileSystem fs = FileSystem.get(new URI("hdfs://linux121:9000"),configuration, "root");
	
	 // 2 执⾏下载操作
	 // boolean delSrc 指是否将原⽂件删除
	 // Path src 指要下载的⽂件路径
	 // Path dst 指将⽂件下载到的路径
	 // boolean useRawLocalFileSystem 是否开启⽂件校验
	fs.copyToLocalFile(false, new Path("/lagou.txt"), new Path("e:/lagou_copy.txt"), true);
	 // 3 关闭资源
	fs.close();
}

3 删除文件

@Test
public void testDelete() throws IOException, InterruptedException,URISyntaxException{
	 // 1 获取⽂件系统
	 Configuration configuration = new Configuration();
	 FileSystem fs = FileSystem.get(new URI("hdfs://linux121:9000"),configuration, "root");
	
	 // 2 执⾏删除
	 fs.delete(new Path("/api_test/"), true);
	
	 // 3 关闭资源
	 fs.close();
}

4 查看⽂件名称、权限、⻓度、块信息

@Test
public void testListFiles() throws IOException, InterruptedException,URISyntaxException{
	 // 1获取⽂件系统
	Configuration configuration = new Configuration();
	FileSystem fs = FileSystem.get(new URI("hdfs://linux121: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();
}

5 ⽂件夹判断

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

	 // 1 获取⽂件配置信息
	Configuration configuration = new Configuration();
	FileSystem fs = FileSystem.get(new URI("hdfs://linux121: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();
}

6 I/O流操作HDFS

以上我们使⽤的API操作都是HDFS系统框架封装好的。我们⾃⼰也可以采⽤IO流的⽅式实现⽂件的上传
和下载。
6.1 ⽂件上传

  1. 需求:把本地e盘上的lagou.txt⽂件上传到HDFS根⽬录
  2. 编写代码
@Test
public void putFileToHDFS() throws IOException, InterruptedException,URISyntaxException {
	 // 1 获取⽂件系统
	Configuration configuration = new Configuration();
	FileSystem fs = FileSystem.get(new URI("hdfs://linux121:9000"),configuration, "root");
	 // 2 创建输⼊流
	FileInputStream fis = new FileInputStream(new File("e:/lagou.txt"));
	 // 3 获取输出流
	FSDataOutputStream fos = fs.create(new Path("/lagou_io.txt"));
	 // 4 流对拷
	IOUtils.copyBytes(fis, fos, configuration);
	 // 5 关闭资源
	IOUtils.closeStream(fos);
	IOUtils.closeStream(fis);
	fs.close();
}

6.2 ⽂件下载
3. 需求:从HDFS上下载lagou.txt⽂件到本地e盘上
4. 编写代码

@Test
public void getFileFromHDFS() throws IOException, InterruptedException,URISyntaxException{
	 // 1 获取⽂件系统
	Configuration configuration = new Configuration();
	FileSystem fs = FileSystem.get(new URI("hdfs://linux121:9000"),configuration, "root");
	
	 // 2 获取输⼊流
	FSDataInputStream fis = fs.open(new Path("/lagou_io.txt"));
	
	 // 3 获取输出流
	FileOutputStream fos = new FileOutputStream(new File("e:/lagou_io_copy.txt"));
	
	 // 4 流的对拷
	IOUtils.copyBytes(fis, fos, configuration);
	
	 // 5 关闭资源
	IOUtils.closeStream(fos);
	IOUtils.closeStream(fis);
	fs.close();
}

6.3 seek 定位读取

  1. 需求:将HDFS上的lagou.txt的内容在控制台输出两次
  2. 编写代码
@Test
public void readFileSeek2() throws IOException, InterruptedException,URISyntaxException{
	 // 1 获取⽂件系统
	Configuration configuration = new Configuration();
	FileSystem fs = FileSystem.get(new URI("hdfs://linux121:9000"),configuration, "root");
	 // 2 打开输⼊流,读取数据输出到控制台
	FSDataInputStream in = null;
	try{
		in= fs.open(new Path("/lagou.txt"));
		IOUtils.copyBytes(in, System.out, 4096, false);
		in.seek(0); //从头再次读取
		IOUtils.copyBytes(in, System.out, 4096, false);
	}finally {
		IOUtils.closeStream(in);
	}

}

3.5 HDFS的读写解析

3.5.1 HDFS读数据流程

在这里插入图片描述

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

3.5.2 HDFS写数据流程

在这里插入图片描述

  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步)。

验证Packet代码

@Test
public void testUploadPacket() throws IOException {
	//1 准备读取本地⽂件的输⼊流
	final FileInputStream in = new FileInputStream(new File("e:/lagou.txt"));
	//2 准备好写出数据到hdfs的输出流
	final FSDataOutputStream out = fs.create(new Path("/lagou.txt"), new Progressable() {
		public void progress() { //这个progress⽅法就是每传输64KB(packet)就会执⾏⼀次,
			System.out.println("&");
		}
	});
	//3 实现流拷⻉
	IOUtils.copyBytes(in, out, configuration); //默认关闭流选项是true,所以会⾃动关闭
	//4 关流 可以再次关闭也可以不关了
}

3.6 NN与2NN

3.6.1 HDFS元数据管理机制

问题1:NameNode如何管理和存储元数据?

  • 计算机中存储数据两种:内存或者是磁盘
  • 元数据存储磁盘:存储磁盘⽆法⾯对客户端对元数据信息的任意的快速低延迟的响应,但是安全性⾼
  • 元数据存储内存:元数据存放内存,可以⾼效的查询以及快速响应客户端的查询请求,数据保存在内存,如果断点,内存中的数据全部丢失。
  • 解决⽅案:内存+磁盘;NameNode内存+FsImage的⽂件(磁盘)
  • 新问题:磁盘和内存中元数据如何划分?

两个数据⼀模⼀样,还是两个数据合并到⼀起才是⼀份完整的数据呢?

  • ⼀模⼀样:client如果对元数据进⾏增删改操作,需要保证两个数据的⼀致性。FsImage⽂件操作起来效率也不⾼。
  • 两个合并=完整数据:NameNode引⼊了⼀个edits⽂件(⽇志⽂件:只能追加写⼊)edits⽂件记录的是client的增删改操作,不再选择让NameNode把数据dump出来形成FsImage⽂件(这种操作是⽐较消耗资源)。

元数据管理流程图

在这里插入图片描述

  • 第⼀阶段:NameNode启动
    • 第⼀次启动NameNode格式化后,创建Fsimage和Edits⽂件。如果不是第⼀次启动,直接加载编辑⽇志和镜像⽂件到内存。
    • 客户端对元数据进⾏增删改的请求。
    • NameNode记录操作⽇志,更新滚动⽇志。
    • NameNode在内存中对数据进⾏增删改。
  • 第⼆阶段:Secondary NameNode⼯作
    • Secondary NameNode询问NameNode是否需要CheckPoint。直接带回NameNode是否执⾏检查点操作结果。
    • Secondary NameNode请求执⾏CheckPoint。
    • NameNode滚动正在写的Edits⽇志。
    • 将滚动前的编辑⽇志和镜像⽂件拷⻉到Secondary NameNode。
    • Secondary NameNode加载编辑⽇志和镜像⽂件到内存,并合并。
    • ⽣成新的镜像⽂件fsimage.chkpoint。
    • 拷⻉fsimage.chkpoint到NameNode。
    • NameNode将fsimage.chkpoint重新命名成fsimage。

3.6.2 Fsimage与Edits⽂件解析

NameNode在执⾏格式化之后,会在/opt/lagou/servers/hadoop-2.9.2/data/tmp/dfs/name/current
⽬录下产⽣如下⽂件
在这里插入图片描述

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

NameNode启动时会将Fsimage⽂件加载到内存中,同时也把之前未合并元数据的Edits⽂件加载,集合两个⽂件中的元数据这样保证了NameNode中的元数据是最新最全的。通俗点说就是NameNode启动时把Fsimage和Edits⽂件进⾏了合并。

3.6.2.1 Fsimage⽂件内容

官⽅地址

https://hadoop.apache.org/docs/r2.9.2/hadoop-project-dist/hadoop-hdfs/HdfsImageViewer.html

在这里插入图片描述
问题:Fsimage中为什么没有记录块所对应DataNode?
在集群启动后,NameNode要求DataNode上报数据块信息,并间隔⼀段时间后再次上报。

3.6.2.2 Edits⽂件内容
  1. 基本语法
    hdfs oev -p ⽂件类型 -i编辑⽇志 -o 转换后⽂件输出路径
  2. 案例实操
[root@linux121 current]$ hdfs oev -p XML -i edits_0000000000000000266-0000000000000000267 -o /opt/lagou/servers/hadoop-2.9.2/edits.xml
[root@linux121 current]$ cat /opt/lagou/servers/hadoop-2.9.2/edits.xml

备注:Edits中只记录了更新相关的操作,查询或者下载⽂件并不会记录在内!!
问题:NameNode启动时如何确定加载哪些Edits⽂件呢?
在这里插入图片描述
需要借助fsimage⽂件最后数字编码,来确定哪些edits之前是没有合并到fsimage中,启动时只需要加载那些未合并的edits⽂件即可。

3.6.3 checkpoint周期

[hdfs-default.xml]

<!-- 定时⼀⼩时 -->
<property>
	<name>dfs.namenode.checkpoint.period</name>
	<value>3600</value>
</property>

<!-- ⼀分钟检查⼀次操作次数,3当操作次数达到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 >

3.7 NN故障处理

NameNode故障后,HDFS集群就⽆法正常⼯作,因为HDFS⽂件系统的元数据需要由NameNode来管理维护并与Client交互,如果元数据出现损坏和丢失同样会导致NameNode⽆法正常⼯作进⽽HDFS⽂件系统⽆法正常对外提供服务。
如果元数据出现丢失损坏如何恢复呢?

  1. 将2NN的元数据拷⻉到NN的节点下
    此种⽅式会存在元数据的丢失。
  2. 搭建HDFS的HA(⾼可⽤)集群,解决NN的单点故障问题!!(借助Zookeeper实现HA,⼀个
    Active的NameNode,⼀个是Standby的NameNode)

3.8 Hadoop的限额与归档以及集群安全模式

⾼级命令

  • HDFS⽂件限额配置
    HDFS⽂件的限额配置允许我们以⽂件⼤⼩或者⽂件个数来限制我们在某个⽬录下上传的⽂件数量或者⽂件内容总量,以便达到我们类似百度⽹盘⽹盘等限制每个⽤户允许上传的最⼤的⽂件的量
  1. 数量限额
hdfs dfs -mkdir -p /user/root/lagou #创建hdfs⽂件夹

hdfs dfsadmin -setQuota 2 /user/root/lagou # 给该⽂件夹下⾯设置最多上传两个⽂件,上传⽂件,发现只能上传⼀个⽂件

hdfs dfsadmin -clrQuota /user/root/lagou # 清除⽂件数量限制
  1. 空间⼤⼩限额
hdfs dfsadmin -setSpaceQuota 4k /user/root/lagou # 限制空间⼤⼩4KB
#上传超过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来说是⼀个⽂件减少了内存的浪费,对于实际操作处理⽂件依然是⼀个⼀个独⽴的⽂件。
    在这里插入图片描述
  1. 归档⽂件
    把/user/lagou/input⽬录⾥⾯的所有⽂件归档成⼀个叫input.har的归档⽂件,并把归档后⽂件存
    储到/user/lagou/output路径下。
[root@linux121 hadoop-2.9.2]$ bin/hadoop archive -archiveName input.har –p /user/root/input /user/root/output
  1. 查看归档
[root@linux121 hadoop-2.9.2]$ hadoop fs -lsr /user/root/output/input.har
[root@linux121 hadoop-2.9.2]$ hadoop fs -lsr har:///user/root/output/input.har
  1. 解归档⽂件
[root@linux121 hadoop-2.9.2]$ hadoop fs -cp har:///user/root/output/input.har/* /user/root
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值