Hadoop-HDFS(详解)

大家好,我是AC.
接着写Hadoop,不是讲完了?当然不是,前面是对Hadoop体系,大致介绍了下,后几篇文章会细讲每个重要的组成部分,今天是介绍HDFS:

一、HDFS概述

1、HDFS产生背景

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

2、HDFS概念

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

3、HDFS优缺点

知道优缺点,方便进行技术选型。

1)优点

(1)高容错性
a)数据自动保存多个副本。它通过增加副本的形式,提高容错性;
b)某一个副本丢失以后,它可以自动恢复。
(2)适合大数据处理
a)数据规模:能够处理数据规模达到GB、TB、甚至PB级别的数据。
b)文件规模:能够处理百万规模以上的文件数量,数量相当之大。
(3)可构建在廉价机器上,通过多副本机制,提高可靠性。

2)缺点

(1)不适合低延时数据访问,比如毫秒级的存储数据,是做不到的。
(2)无法高效地对大量小文件进行存储。
a)存储大量小文件的话,它会占用NameNode大量的内存来存储文件、目录和块信息。这样是不可取的,因为NameNode的内存总是有限的。1 kb 1kb 1kb 150字节 100MB 150字节
b)小文件存储的寻址时间会超过读取时间,它违反了HDFS的设计目标。
i.寻址时间,目前技术水平在10ms左右
ii.传输
寻址时间/传输时间=1%,传输时间1000ms=1s,磁盘传输速度100M/S,计算机是2
的n次方,所以hadoop2.x默认块的大小为128M。
(3)不支持并发写入、文件随机修改。
a)一个文件只能有一个写,不允许多个线程同时写;
b)仅支持数据append(追加),不支持文件的随机修改

4、HDFS组成架构

HDFS组成架构如图所示:
在这里插入图片描述
架构主要由四个部分组成,分别为HDFS ClientNameNodeDataNodeSecondary NameNode。下面我们分别介绍这四个组成部分。
(1)Client:就是客户端。
a)文件切分。文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行存储;
b)与NameNode交互,获取文件的位置信息;
c)与DataNode交互,读取或者写入数据;

d)Client提供一些命令来管理HDFS,比如启动或者关闭HDFS;
e)Client可以通过一些命令来访问HDFS。
(2)NameNode:就是Master,它是一个主管、管理者。
a)管理HDFS的名称空间;namespace
b)管理数据块(Block)映射信息;
c)配置副本策略(默认);3
d)处理客户端读写请求。
(3)DataNode:就是Slave(奴隶)。NameNode下达命令,DataNode执行实际的操作。
a)存储实际的数据块;
b)执行数据块的读/写操作。
(4)SecondaryNameNode:并非NameNode的热备。当NameNode挂掉的时候,它并不能马上替换NameNode并提供服务。
a)辅助NameNode,分担其工作量;
b)定期合并fsimageEdits,并推送给NameNode
c)在紧急情况下,可辅助恢复NameNode

5、HDFS文件块大小

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

二、HDFS的Shell客户端操作

1、基本语法

bin/hadoop fs 具体命令

2、命令大全

[root@hadoop101 hadoop-2.7.2]# bin/hadoop fs
[-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] <localsrc> ... <dst>]
        [-copyToLocal [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
        [-count [-q] <path> ...]
        [-cp [-f] [-p] <src> ... <dst>]
        [-createSnapshot <snapshotDir> [<snapshotName>]]
        [-deleteSnapshot <snapshotDir> <snapshotName>]
        [-df [-h] [<path> ...]]
        [-du [-s] [-h] <path> ...]
        [-expunge]
        [-get [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
        [-getfacl [-R] <path>]
        [-getmerge [-nl] <src> <localdst>]
        [-help [cmd ...]]
        [-ls [-d] [-h] [-R] [<path> ...]]
        [-mkdir [-p] <path> ...]
        [-moveFromLocal <localsrc> ... <dst>]
        [-moveToLocal <src> <localdst>]
        [-mv <src> ... <dst>]
        [-put [-f] [-p] <localsrc> ... <dst>]
        [-renameSnapshot <snapshotDir> <oldName> <newName>]
        [-rm [-f] [-r|-R] [-skipTrash] <src> ...]
        [-rmdir [--ignore-fail-on-non-empty] <dir> ...]
        [-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
        [-setrep [-R] [-w] <rep> <path> ...]
        [-stat [format] <path> ...]
        [-tail [-f] <file>]
        [-test -[defsz] <path>]
        [-text [-ignoreCrc] <src> ...]
        [-touchz <path> ...]
        [-usage [cmd ...]]

3、常用命令实操

1)启动Hadoop集群(方便后续的测试)

[root@hadoop101 hadoop-2.7.2]# sbin/start-dfs.sh
[root@hadoop102 hadoop-2.7.2]# sbin/start-yarn.sh

2)-help:输出这个命令参数

[root@hadoop101 hadoop-2.7.2]# hadoop fs -help rm

3)-ls: 显示目录信息

[root@hadoop101 hadoop-2.7.2]# hadoop fs -ls /

4)-mkdir:在hdfs上创建目录

[root@hadoop101 hadoop-2.7.2]# hadoop fs -mkdir -p /sanguo/shuguo

5)-moveFromLocal从本地剪切粘贴到hdfs

[root@hadoop101 hadoop-2.7.2]# touch kongming.txt
[root@hadoop101 hadoop-2.7.2]# hadoop fs  -moveFromLocal  ./kongming.txt  /sanguo/shuguo

6)-appendToFile :追加一个文件到已经存在的文件末尾

[root@hadoop101 hadoop-2.7.2]# touch liubei.txt
[root@hadoop101 hadoop-2.7.2]# vim liubei.txt
输入
san gu mao lu
[root@hadoop102 hadoop-2.7.2]# hadoop fs -appendToFile liubei.txt /sanguo/shuguo/kongming.txt

7)-cat:显示文件内容

[root@hadoop101 hadoop-2.7.2]# hadoop fs -cat /sanguo/shuguo/kongming.txt

8)-tail:显示一个文件的末尾

[root@hadoop101 hadoop-2.7.2]# hadoop fs -tail /sanguo/shuguo/kongming.txt

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

[root@hadoop101 hadoop-2.7.2]# hadoop fs  -chmod  666  /sanguo/shuguo/kongming.txt
[root@hadoop101 hadoop-2.7.2]# hadoop fs  -chown  root:root   /sanguo/shuguo/kongming.txt

10)-copyFromLocal:从本地文件系统中拷贝文件到hdfs路径去

[root@hadoop101 hadoop-2.7.2]# hadoop fs -copyFromLocal README.txt /

11)-copyToLocal:从hdfs拷贝到本地

[root@hadoop101 hadoop-2.7.2]# hadoop fs -copyToLocal /sanguo/shuguo/kongming.txt ./

12)-cp :从hdfs的一个路径拷贝到hdfs的另一个路径

[root@hadoop101 hadoop-2.7.2]# hadoop fs -cp /sanguo/shuguo/kongming.txt /zhuge.txt

13)-mv:在hdfs目录中移动文件

[root@hadoop101 hadoop-2.7.2]# hadoop fs -mv /zhuge.txt /sanguo/shuguo/

14)-get:等同于copyToLocal,就是从hdfs下载文件到本地

[root@hadoop101 hadoop-2.7.2]# hadoop fs -get /sanguo/shuguo/kongming.txt ./

15)-getmerge :合并下载多个文件,比如hdfs的目录 /aaa/下有多个文件:log.1, log.2,log.3,…

[root@hadoop101 hadoop-2.7.2]# hadoop fs -getmerge /sanguo/shuguo/* ./zaiyiqi.txt

16)-put:等同于copyFromLocal

[root@hadoop101 hadoop-2.7.2]# hadoop fs -put ./zaiyiqi.txt /sanguo/shuguo/

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

[root@hadoop101 hadoop-2.7.2]# hadoop fs -rm /user/root/test/jinlian2.txt

18)-rmdir:删除空目录(了解)

[root@hadoop101 hadoop-2.7.2]# hadoop fs -mkdir /test
[root@hadoop101 hadoop-2.7.2]# hadoop fs -rmdir /test

19)-du统计文件夹的大小信息

[root@hadoop101 hadoop-2.7.2]# hadoop fs -du -s -h /user/root/test
2.7 K  /user/root/test
[root@hadoop102 hadoop-2.7.2]# hadoop fs -du  -h /user/root/test
1.3 K  /user/root/test/README.txt
15     /user/root/test/jinlian.txt
1.4 K  /user/root/test/zaiyiqi.txt

20)-setrep:设置hdfs中文件的副本数量

[root@hadoop101 hadoop-2.7.2]# hadoop fs -setrep 10 /sanguo/shuguo/kongming.txt

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

三、HDFS的Java客户端操作

1、HDFS客户端环境准备

1)根据自己电脑的操作系统拷贝对应的编译后的hadoop jar包到非中文路径(例如:D:\Develop\hadoop-2.7.2),如图所示:
在这里插入图片描述
2)配置HADOOP_HOME环境变量,如图所示:
在这里插入图片描述
3)配置Path环境变量,如图所示:
在这里插入图片描述

4)创建一个Maven工程HdfsClientDemo
工程就不说了
5)导入相应的依赖坐标+日志添加

<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</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.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.hadoop</groupId>
			<artifactId>hadoop-client</artifactId>
			<version>2.7.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.hadoop</groupId>
			<artifactId>hadoop-hdfs</artifactId>
			<version>2.7.2</version>
		</dependency>
</dependencies>

注意:如果eclipse/idea打印不出日志,在控制台上只显示:

1.log4j:WARN No appenders could be found for logger (org.apache.hadoop.util.Shell)
2.log4j:WARN Please initialize the log4j system properly
3.log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info

需要在项目的src/main/resources目录下,新建一个文件,命名为“log4j.properties”,在文件中填入:

log4j.rootLogger=debug, 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

6)创建包名:com.hadoop.hdfs
包名也太简单了,就不说了
7)创建HdfsClient类

@Test
public void testName() throws Exception {
	//1 创建一个配置对象
	Configuration conf = new Configuration();
	//2 获取hdfs的客户端
	FileSystem fs = FileSystem.get(new URI("hdfs://hadoop101:9000"), conf, "root");
	//3 通过客户端操作hdfs
	boolean mkdirs = fs.mkdirs(new Path("/user/bbb"));
	System.out.println("是否创建成功:"+mkdirs);
	//4 关闭资源
	fs.close();
}

2、HDFS的API操作

1)HDFS文件上传
(1)编写源代码

@Test
public void upload() throws Exception {
	//1 创建一个配置对象
	Configuration conf = new Configuration();
	//参数设置的优先级 代码>项目>服务器默认
	conf.set("dfs.replication", "4");
	//2 获取hdfs的客户端
	FileSystem fs = FileSystem.get(new URI("hdfs://hadoop101:9000"), conf, "root");
	//3 通过客户端操作hdfs
	fs.copyFromLocalFile(new Path("D:/test/test.jpg"), new Path("/user/test4.jpg"));
	System.out.println("上传成功:");
	//4 关闭资源
	fs.close();
}

(2)将hdfs-site.xml拷贝到项目的根目录下

<?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>

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

@Test
	public void download() throws Exception {
		//1 创建一个配置对象
		Configuration conf = new Configuration();
		//2 获取hdfs的客户端
		FileSystem fs = FileSystem.get(new URI("hdfs://hadoop101:9000"), conf, "root");
		//3 通过客户端操作hdfs
		//参数1:是否删除源文件
		//参数2:源文件路径
		//参数3:目标文件路径
		//参数4:是否开启文件校验
		fs.copyToLocalFile(false, new Path("/user/test4.jpg"), new Path("D:/test/test1225.jpg"), true);
		System.out.println("下载成功:");
		//4 关闭资源
		fs.close();		
	}

3)HDFS文件夹删除

@Test
	public void delete() throws Exception {
		//1 创建一个配置对象
		Configuration conf = new Configuration();
		//2 获取hdfs的客户端
		FileSystem fs = FileSystem.get(new URI("hdfs://hadoop101:9000"), conf, "root");
		//3 通过客户端操作hdfs
		//参数1:要删除的路径
		//参数2:是否递归删除,如果要删除目录,该参数需要指定为true
		boolean delete = fs.delete(new Path("/user/aaa"), true);
		System.out.println("是否删除成功:"+delete);
		//4 关闭资源
		fs.close();		
	}

4)HDFS文件名更改

@Test
public void rename() throws Exception {
	//1 创建一个配置对象
	Configuration conf = new Configuration();
	//2 获取hdfs的客户端
	FileSystem fs = FileSystem.get(new URI("hdfs://hadoop101:9000"), conf, "root");
	//3 通过客户端操作hdfs
	boolean rename = fs.rename(new Path("/user/bbb"), new Path("/user/ccc"));
	System.out.println("是否重命名成功:"+rename);
	//4 关闭资源
	fs.close();		
}

5)HDFS文件和文件夹判断

	@Test
	public void isFile() throws Exception {
		//1 创建一个配置对象
		Configuration conf = new Configuration();
		//2 获取hdfs的客户端
		FileSystem fs = FileSystem.get(new URI("hdfs://hadoop101:9000"), conf, "root");
		//3 通过客户端操作hdfs
//		boolean directory = fs.isDirectory(new Path("/user/ccc"));
//		System.out.println("是否为目录:"+directory);
		FileStatus[] listStatus = fs.listStatus(new Path("/user"));
		for (FileStatus fileStatus : listStatus) {
			if(fileStatus.isDirectory()){
				System.out.println(fileStatus.getPath().getName()+"是目录");
			}else{
				System.out.println(fileStatus.getPath().getName()+"不是目录");
			}
		}
		//4 关闭资源
		fs.close();			
	}

6)通过IO流操作HDFS

  • HDFS文件上传
@SuppressWarnings("resource")
@Test
public void ioUpload() throws Exception {
	//1 创建一个配置对象
	Configuration conf = new Configuration();
	//2 获取hdfs的客户端
	FileSystem fs = FileSystem.get(new URI("hdfs://hadoop101:9000"), conf, "root");
	//3 通过客户端操作hdfs
	
	//创建一个输入流,把本地磁盘的文件读取到内存
	FileInputStream fis = new FileInputStream(new File("D:/test/test.jpg"));
	//创建输出流,把读取进内存的数据写出到hdfs
	FSDataOutputStream fsdos = fs.create(new Path("/user/testio.jpg"));
	//边读边写
	IOUtils.copyBytes(fis, fsdos, conf);
	//4 关闭资源
	IOUtils.closeStream(fis);
	IOUtils.closeStream(fsdos);
	fs.close();			
}
  • HDFS文件下载到本地
@Test
	public void ioDownload() throws Exception {
		//1 创建一个配置对象
		Configuration conf = new Configuration();
		//2 获取hdfs的客户端
		FileSystem fs = FileSystem.get(new URI("hdfs://hadoop101:9000"), conf, "root");
		//3 通过客户端操作hdfs
		
		//创建一个输入流,把hdfs的文件读取到内存
		FSDataInputStream open = fs.open(new Path("/user/testio.jpg"));
		//创建输出流,把读取进内存的数据写出到本地磁盘
		FileOutputStream fos = new FileOutputStream(new File("D:/test/iotest.jpg"));
		//边读边写
		IOUtils.copyBytes(open, fos, conf);
		//4 关闭资源
		IOUtils.closeStream(open);
		IOUtils.closeStream(fos);
		fs.close();			
	}
  • 定位文件读取
  • 下载第一块
@Test
	public void download1() throws Exception {
		//1 创建一个配置对象
		Configuration conf = new Configuration();
		//2 获取hdfs的客户端
		FileSystem fs = FileSystem.get(new URI("hdfs://hadoop101:9000"), conf, "root");
		//3 通过客户端操作hdfs
		
		//创建一个输入流,把hdfs的文件读取到内存
		FSDataInputStream open = fs.open(new Path("/user/hadoop-2.7.2.tar.gz"));
		//创建输出流,把读取进内存的数据写出到本地磁盘
		FileOutputStream fos = new FileOutputStream(new File("D:/test/part1.tar.gz"));
		
		
		byte[] b = new byte[1024];//1024字节 1kb
		for(int i=0;i < 1024*128;i++){
			open.read(b);
			fos.write(b);
		}
		
		//4 关闭资源
		IOUtils.closeStream(open);
		IOUtils.closeStream(fos);
		fs.close();			
	}
  • 下载第二块
@Test
	public void download2() throws Exception {
		//1 创建一个配置对象
		Configuration conf = new Configuration();
		//2 获取hdfs的客户端
		FileSystem fs = FileSystem.get(new URI("hdfs://hadoop101:9000"), conf, "root");
		//3 通过客户端操作hdfs
		
		//创建一个输入流,把hdfs的文件读取到内存
		FSDataInputStream open = fs.open(new Path("/user/hadoop-2.7.2.tar.gz"));
		open.seek(128*1024*1024);//128M=128*1024kb=128*1024*1024
		//创建输出流,把读取进内存的数据写出到本地磁盘
		FileOutputStream fos = new FileOutputStream(new File("D:/test/part2.tar.gz"));
		
		IOUtils.copyBytes(open, fos, conf);
		//4 关闭资源
		IOUtils.closeStream(open);
		IOUtils.closeStream(fos);
		fs.close();			
	}
  • 合并文件
    在window命令行中执行如下命令(所在文件夹),然后修改part2文件为hadoop-2.7.2.tar.gz并解压
    type hadoop-2.7.2.tar.gz.part2 >> hadoop-2.7.2.tar.gz.part1

四、HDFS的数据流

1、HDFS写数据流程

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;
(8)当一个block传输完成之后,客户端再次请求NameNode上传第二个block的服务器。(重复执行3-7步)。

2、HDFS读数据流程

HDFS的读数据流程,如图所示:
HDFS读数据流程
在这里插入图片描述

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

五、NameNode和SecondaryNameNode工作机制

在这里插入图片描述

1、NN和2NN工作机制

NN和2NN工作机制,如图所示
NN和2NN工作机制
在这里插入图片描述

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

NN和2NN工作机制详解:
fsimage:namenode内存中元数据序列化后形成的文件。
edits:记录客户端更新元数据信息的每一步操作(可通过Edits运算出元数据)。
namenode启动时,先滚动edits并生成一个空的edits.inprogress,然后加载edits(归档后的)和fsimage(最新的)到内存中,此时namenode内存就持有最新的元数据信息。client开始对namenode发送元数据的增删改查的请求,这些请求的操作首先会被记录在edits.inprogress中(查询元数据的操作不会被记录在edits中,因为查询操作不会更改元数据信息),如果此时namenode挂掉,重启后会从edits中读取元数据的信息。然后,namenode会在内存中执行元数据的增删改查的操作。
由于edits中记录的操作会越来越多,edits文件会越来越大,导致namenode在启动加载edits时会很慢,所以需要对edits和fsimage进行合并(所谓合并,就是将edits和fsimage加载到内存中,照着edits中的操作一步步执行,最终形成新的fsimage)。Secondarynamenode:帮助namenode进行edits和fsimage的合并工作。
secondarynamenode首先会询问namenode是否需要checkpoint(触发checkpoint需要满足两个条件中的任意一个,定时时间到和edits中数据写满了)。直接带回namenode是否检查结果。secondarynamenode执行checkpoint操作,首先会让namenode滚动edits并生成一个空的edits.inprogress,滚动edits的目的是给edits打个标记,以后所有新的操作都写入edits.inprogress,其他未合并的edits和fsimage会拷贝到secondarynamenode的本地,然后将拷贝的edits和fsimage加载到内存中进行合并,生成fsimage.chkpoint,然后将fsimage.chkpoint拷贝给namenode,重命名为fsimage后替换掉原来的fsimage。namenode在启动时就只需要加载之前未合并的edits和fsimage即可,因为合并过的edits中的元数据信息已经被记录在fsimage中。

2、Fsimage和Edits解析

1)概念
namenode被格式化之后,将在/opt/module/hadoop-2.7.2/data/tmp/dfs/name/current目录中产生如下文件:

fsimage_0000000000000000000
fsimage_0000000000000000000.md5
seen_txid
VERSION

(1)fsimage文件:HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目录和文件的序列化信息。
(2)edits文件:存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到edits文件中。
(3)seen_txid文件保存的是一个数字,就是最后一个edits_的数字
(4)每次NameNode启动的时候都会将fsimage文件读入内存,并执行edits里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成NameNode启动的时候就将fsimage和edits文件进行了合并。
2)oiv查看fsimage文件
(1)查看oiv和oev命令

[root@hadoop101 current]# hdfs
Oiv应用离线查看映射器,oev 应用离线编辑查看器,查看日志

oiv apply the offline fsimage viewer to an fsimage
oev apply the offline edits viewer to an edits file
(2)基本语法

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

(3)案例实操

[root@hadoop101 current]# pwd
/opt/module/hadoop-2.7.2/data/tmp/dfs/name/current
[root@hadoop101 current]# hdfs oiv -p XML -i fsimage_0000000000000000025 -o /opt/module/hadoop-2.7.2/fsimage.xml
[root@hadoop101 current]# cat /opt/module/hadoop-2.7.2/fsimage.xml

将显示的xml文件内容拷贝到Idea中创建的xml文件中,并格式化。部分显示结果如下。Xml参数必须大写。

<inode>
	<id>16386</id>
	<type>DIRECTORY</type>
	<name>user</name>
	<mtime>1512722284477</mtime>
	<permission>root:supergroup:rwxr-xr-x</permission>
	<nsquota>-1</nsquota>
	<dsquota>-1</dsquota>
</inode>
<inode>
	<id>16387</id>
	<type>DIRECTORY</type>
	<name>root</name>
	<mtime>1512790549080</mtime>
	<permission>root:supergroup:rwxr-xr-x</permission>
	<nsquota>-1</nsquota>
	<dsquota>-1</dsquota>
</inode>
<inode>
	<id>16389</id>
	<type>FILE</type>
	<name>wc.input</name>
	<replication>3</replication>
	<mtime>1512722322219</mtime>
	<atime>1512722321610</atime>
	<perferredBlockSize>134217728</perferredBlockSize>
	<permission>root:supergroup:rw-r--r--</permission>
	<blocks>
		<block>
			<id>1073741825</id>
			<genstamp>1001</genstamp>
			<numBytes>59</numBytes>
		</block>
	</blocks>
</inode >

3)oev查看edits文件
(1)基本语法
hdfs oev -p 文件类型 -i编辑日志 -o 转换后文件输出路径
(2)案例实操

[root@hadoop101 current]# hdfs oev -p XML -i edits_0000000000000000012-0000000000000000013 -o /opt/module/hadoop-2.7.2/edits.xml
[root@hadoop101 current]# cat /opt/module/hadoop-2.7.2/edits.xml

将显示的xml文件内容拷贝到Idea中创建的xml文件中,并格式化。显示结果如下。

<?xml version="1.0" encoding="UTF-8"?>
<EDITS>
	<EDITS_VERSION>-63</EDITS_VERSION>
	<RECORD>
		<OPCODE>OP_START_LOG_SEGMENT</OPCODE>
		<DATA>
			<TXID>129</TXID>
		</DATA>
	</RECORD>
	<RECORD>
		<OPCODE>OP_ADD</OPCODE>
		<DATA>
			<TXID>130</TXID>
			<LENGTH>0</LENGTH>
			<INODEID>16407</INODEID>
			<PATH>/hello7.txt</PATH>
			<REPLICATION>2</REPLICATION>
			<MTIME>1512943607866</MTIME>
			<ATIME>1512943607866</ATIME>
			<BLOCKSIZE>134217728</BLOCKSIZE>
			<CLIENT_NAME>DFSClient_NONMAPREDUCE_-1544295051_1</CLIENT_NAME>
			<CLIENT_MACHINE>192.168.1.5</CLIENT_MACHINE>
			<OVERWRITE>true</OVERWRITE>
			<PERMISSION_STATUS>
				<USERNAME>root</USERNAME>
				<GROUPNAME>supergroup</GROUPNAME>
				<MODE>420</MODE>
			</PERMISSION_STATUS>
			<RPC_CLIENTID>908eafd4-9aec-4288-96f1-e8011d181561</RPC_CLIENTID>
			<RPC_CALLID>0</RPC_CALLID>
		</DATA>
	</RECORD>
	<RECORD>
		<OPCODE>OP_ALLOCATE_BLOCK_ID</OPCODE>
		<DATA>
			<TXID>131</TXID>
			<BLOCK_ID>1073741839</BLOCK_ID>
		</DATA>
	</RECORD>
	<RECORD>
		<OPCODE>OP_SET_GENSTAMP_V2</OPCODE>
		<DATA>
			<TXID>132</TXID>
			<GENSTAMPV2>1016</GENSTAMPV2>
		</DATA>
	</RECORD>
	<RECORD>
		<OPCODE>OP_ADD_BLOCK</OPCODE>
		<DATA>
			<TXID>133</TXID>
			<PATH>/hello7.txt</PATH>
			<BLOCK>
				<BLOCK_ID>1073741839</BLOCK_ID>
				<NUM_BYTES>0</NUM_BYTES>
				<GENSTAMP>1016</GENSTAMP>
			</BLOCK>
			<RPC_CLIENTID></RPC_CLIENTID>
			<RPC_CALLID>-2</RPC_CALLID>
		</DATA>
	</RECORD>
	<RECORD>
		<OPCODE>OP_CLOSE</OPCODE>
		<DATA>
			<TXID>134</TXID>
			<LENGTH>0</LENGTH>
			<INODEID>0</INODEID>
			<PATH>/hello7.txt</PATH>
			<REPLICATION>2</REPLICATION>
			<MTIME>1512943608761</MTIME>
			<ATIME>1512943607866</ATIME>
			<BLOCKSIZE>134217728</BLOCKSIZE>
			<CLIENT_NAME></CLIENT_NAME>
			<CLIENT_MACHINE></CLIENT_MACHINE>
			<OVERWRITE>false</OVERWRITE>
			<BLOCK>
				<BLOCK_ID>1073741839</BLOCK_ID>
				<NUM_BYTES>25</NUM_BYTES>
				<GENSTAMP>1016</GENSTAMP>
			</BLOCK>
			<PERMISSION_STATUS>
				<USERNAME>root</USERNAME>
				<GROUPNAME>supergroup</GROUPNAME>
				<MODE>420</MODE>
			</PERMISSION_STATUS>
		</DATA>
	</RECORD>
</EDITS >

3、checkpoint时间设置

(1)通常情况下,SecondaryNameNode每隔一小时执行一次。如果修改,在hdfs-site中修改
默认值在[hdfs-default.xml]

<property>
  <name>dfs.namenode.checkpoint.period</name>
  <value>3600</value>
</property>

(2)一分钟检查一次操作次数,当操作次数达到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 >

4、集群安全模式

1)概述
NameNode启动时,首先将映像文件(fsimage)载入内存,并执行编辑日志(edits)中的各项操作。一旦在内存中成功建立文件系统元数据的映像,则创建一个新的fsimage文件和一个空的编辑日志。此时,NameNode开始监听DataNode请求。但是此刻,NameNode运行在安全模式,即NameNode的文件系统对于客户端来说是只读的。
系统中的数据块的位置并不是由NameNode维护的,而是以块列表的形式存储在DataNode中。在系统的正常操作期间,NameNode会在内存中保留所有块位置的映射信息。在安全模式下,各个DataNode会向NameNode发送最新的块列表信息,NameNode了解到足够多的块位置信息之后,即可高效运行文件系统。
如果满足“最小副本条件”,NameNode会在30秒钟之后就退出安全模式。所谓的最小副本条件指的是在整个文件系统中99.9%的块满足最小副本级别(默认值:dfs.replication.min=1)。在启动一个刚刚格式化的HDFS集群时,因为系统中还没有任何块,所以NameNode不会进入安全模式。
2)基本语法
集群处于安全模式,不能执行重要操作(写操作)。集群启动完成后,自动退出安全模式。
(1)bin/hdfs dfsadmin -safemode get (功能描述:查看安全模式状态)
(2)bin/hdfs dfsadmin -safemode enter (功能描述:进入安全模式状态)
(3)bin/hdfs dfsadmin -safemode leave (功能描述:离开安全模式状态)
(4)bin/hdfs dfsadmin -safemode wait (功能描述:等待安全模式状态,监控安全模式)
3)案例
模拟等待安全模式
(1)先进入安全模式

[root@hadoop101 hadoop-2.7.2]# bin/hdfs dfsadmin -safemode enter

(2)执行下面的脚本
编辑一个脚本

#!/bin/bash
bin/hdfs dfsadmin -safemode wait(安全模式关闭)
bin/hdfs dfs -put ~/hello.txt /root/hello.txt

(3)再打开一个窗口,执行

[root@hadoop101 hadoop-2.7.2]# bin/hdfs dfsadmin -safemode leave

六、DataNode工作机制

DataNode工作机制,如图所示:
在这里插入图片描述

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

完结,HDFS暂介绍这里,如写的哪里有问题,还望各位多多指点一二,共同学习,加油!谢谢观看。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值