大数据之HDFS

一、HDFS概念

1.1 概念

  HDFS,它是一个文件系统,全称:Hadoop Distributed File System,用于存储文件通过目录树来定位文件;其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。

1.2 组成

  • 1)HDFS集群包括,NameNode和DataNode以及Secondary Namenode。
  • 2)NameNode负责管理整个文件系统的元数据,以及每一个路径(文件)所对应的数据块信息。
  • 3)DataNode 负责管理用户的文件数据块,每一个数据块都可以在多个datanode上存储多个副本。
  • 4)Secondary NameNode用来监控HDFS状态的辅助后台程序,每隔一段时间获取HDFS元数据的快照。

1.3 HDFS 文件块大小

  HDFS中的文件在物理上是分块存储(block),块的大小可以通过配置参数( dfs.blocksize)来规定,默认大小在hadoop2.x版本中是128M,老版本中是64M
  HDFS的块比磁盘的块大,其目的是为了最小化寻址开销。如果块设置得足够大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间。因而,传输一个由多个块组成的文件的时间取决于磁盘传输速率。
  如果寻址时间约为10ms,而传输速率为100MB/s,为了使寻址时间仅占传输时间的1%,我们要将块大小设置约为100MB。默认的块大小128MB。

二、HFDS命令行操作

  • 1)基本语法
bin/hadoop fs 具体命令
  • 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)-help:输出这个命令参数

bin/hdfs dfs -help rm

(2)-ls: 显示目录信息

hadoop fs -ls /

(3)-mkdir:在hdfs上创建目录

hadoop fs  -mkdir  -p  /hdfs路径

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

hadoop  fs  -moveFromLocal  本地路径  /hdfs路径

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

hadoop  fs  -appendToFile  本地路径  /hdfs路径

(6)-cat :显示文件内容

hadoop fs -cat /hdfs路径

(7)-tail -f:监控文件

hadoop  fs  -tail -f /hdfs路径

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

hadoop  fs  -chmod  777  /hdfs路径
hadoop  fs  -chown  someuser:somegrp   /hdfs路径

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

hadoop  fs  -cp  /hdfs路径1  / hdfs路径2

(10)-mv:在hdfs目录中移动/重命名 文件

hadoop  fs  -mv  /hdfs路径  / hdfs路径

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

hadoop fs -get / hdfs路径 ./本地路径

(12)-getmerge :合并下载多个文到linux本地,比如hdfs的目录 /aaa/下有多个文件:log.1, log.2,log.3,...(注:是合成到Linux本地)

hadoop fs -getmerge /aaa/log.* ./log.sum
合成到不同的目录:hadoop fs -getmerge /hdfs1路径 /hdfs2路径 /

(13)-put:等同于copyFromLocal(它们的区别在于copyToLocal的源路径只能是一个本地的文件。而put的源路径可能是多个文件,也可能是标准输入。)

hadoop  fs  -put  /本地路径  /hdfs路径

(14)-rm:删除文件或文件夹

hadoop fs -rm -r /hdfs路径

(15)-df :统计文件系统的可用空间信息

hadoop  fs  -df  -h  / hdfs路径

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

[itstar@bigdata111 hadoop-2.7.2]$ hadoop fs -du -s -h / hdfs路径
188.5 M  /user/itstar/wcinput
[itstar@bigdata111 hadoop-2.7.2]$ hadoop fs -du  -h / hdfs路径
188.5 M  / hdfs路径
97       / hdfs路径

(17)-count:统计一个指定目录下的文件节点数量

hadoop fs -count /aaa/
[itstar@bigdata111 hadoop-2.7.2]$ hadoop fs -count / hdfs路径
       1            	2          197657784 / hdfs路径
嵌套文件层级;  包含文件的总数

(18)-setrep:设置hdfs中文件的副本数量:3是副本数,可改

hadoop fs -setrep 3 / hdfs路径


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

三、HDFS客户端操作

3.1、IDEA环境准备

{$MAVEN_HOME/conf/settings}

  <!--本地仓库所在位置-->
<localRepository>F:m2
epository</localRepository>

<!--使用阿里云镜像去下载Jar包,速度更快-->
  <mirrors>
    <mirror>
      <id>alimaven</id>
      <name>aliyun maven</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
      <mirrorOf>central</mirrorOf>        
    </mirror>
  </mirrors>

<!--本地配置JDK8版本-->
	<profiles>
		<profile>  
		  <id>jdk-1.8</id>  
		   <activation>  
		     <activeByDefault>true</activeByDefault>  
		     <jdk>1.8</jdk>  
		   </activation>  
			<properties>  
				<maven.compiler.source>1.8</maven.compiler.source>  
				<maven.compiler.target>1.8</maven.compiler.target>  
				<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
			</properties>
		</profile>
	</profiles>

3.1.1、Maven准备

<dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>2.8.4</version>
        </dependency>

        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>2.8.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-hdfs</artifactId>
            <version>2.8.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.8.4</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.7</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

3.1.2、IDEA准备

  • 1)配置HADOOP_HOME环境变量
  • 2)采用hadoop编译后的bin 、lib两个文件夹(如果不生效,重新启动IDEA)
  • 3)创建第一个java工程
public class HdfsClientDemo1 {
	public static void main(String[] args) throws Exception {
		// 1 获取文件系统
		Configuration configuration = new Configuration();
		// 配置在集群上运行
		configuration.set("fs.defaultFS", "hdfs://bigdata111:9000");
		FileSystem fileSystem = FileSystem.get(configuration);
		
		// 直接配置访问集群的路径和访问集群的用户名称
//		FileSystem fileSystem = FileSystem.get(new URI("hdfs://bigdata111:9000"),configuration, "itstar");
		
		// 2 把本地文件上传到文件系统中
		fileSystem.copyFromLocalFile(new Path("f:/hello.txt"), new Path("/hello1.copy.txt"));
		
		// 3 关闭资源
		fileSystem.close();
		System.out.println("over");
	}
}
  • 4)执行程序
      注:eclipse运行时可能需要配置用户名称
      客户端去操作hdfs时,是有一个用户身份的。默认情况下,hdfs客户端api会从jvm中获取一个参数来作为自己的用户身份:-DHADOOP_USER_NAME=itstar,itstar为用户名称。

3.2、通过API操作HDFS

3.2.1 HDFS获取文件系统

  • 1)详细代码
	@Test
	public void initHDFS() throws Exception{
		// 1 创建配置信息对象
		Configuration configuration = new Configuration();
		
		// 2 获取文件系统
		FileSystem fs = FileSystem.get(configuration);
		
		// 3 打印文件系统
		System.out.println(fs.toString());
	}

3.2.2、HDFS文件上传

	@Test
	public void putFileToHDFS() throws Exception{
		// 1 创建配置信息对象
		// new Configuration();的时候,它就会去加载jar包中的hdfs-default.xml
		// 然后再加载classpath下的hdfs-site.xml
		Configuration configuration = new Configuration();

		// 2 设置参数 
		// 参数优先级: 1、客户端代码中设置的值  2、classpath下的用户自定义配置文件 3、然后是服务器的默认配置
		configuration.set("dfs.replication", "2");

		FileSystem fs = FileSystem.get(new URI("hdfs://bigdata111:9000"),configuration, "itstar");
		
		// 3 创建要上传文件所在的本地路径
		Path src = new Path("e:/hello.txt");
		
		// 4 创建要上传到hdfs的目标路径
		Path dst = new Path("hdfs://bigdata111:9000/user/itstar/hello.txt");
		
		// 5 拷贝文件
		fs.copyFromLocalFile(src, dst);
		fs.close();	
}

2)测试参数优先级
参数优先级: 1、客户端代码中设置的值 2、然后是服务器的默认配置

3.2.3、HDFS文件下载

@Test
public void getFileFromHDFS() throws Exception{
		
	// 1 创建配置信息对象
	Configuration configuration = new Configuration();
		
	FileSystem fs = FileSystem.get(new URI("hdfs://bigdata111:9000"),configuration, "itstar");	
	
//	fs.copyToLocalFile(new Path("hdfs://bigdata111:9000/user/itstar/hello.txt"), new Path("d:/hello.txt"));
	// boolean delSrc 指是否将原文件删除
	// Path src 指要下载的文件路径
	// Path dst 指将文件下载到的路径
	// boolean useRawLocalFileSystem 是否开启文件效验
    // 2 下载文件
	fs.copyToLocalFile(false, new Path("hdfs://bigdata111:9000/user/itstar/hello.txt"), new Path("e:/hellocopy.txt"), true);
	fs.close();
	}

3.2.4、HDFS目录创建

	@Test
	public void mkdirAtHDFS() throws Exception{
		// 1 创建配置信息对象
		Configuration configuration = new Configuration();
		
		FileSystem fs = FileSystem.get(new URI("hdfs://bigdata111:9000"),configuration, "itstar");	
		
		//2 创建目录
		fs.mkdirs(new Path("hdfs://bigdata111:9000/user/itstar/output"));
	fs.close();
	}

3.2.5、HDFS文件夹删除

	@Test
	public void deleteAtHDFS() throws Exception{
		// 1 创建配置信息对象
		Configuration configuration = new Configuration();
		
		FileSystem fs = FileSystem.get(new URI("hdfs://bigdata111:9000"),configuration, "itstar");	
		
		//2 删除文件夹 ,如果是非空文件夹,参数2是否递归删除,true递归
		fs.delete(new Path("hdfs://bigdata111:9000/user/itstar/output"), true);
	fs.close();
	}

3.2.6、HDFS文件名更改

	@Test
	public void renameAtHDFS() throws Exception{
		// 1 创建配置信息对象
		Configuration configuration = new Configuration();
		
		FileSystem fs = FileSystem.get(new URI("hdfs://bigdata111:9000"),configuration, "itstar");
		
		//2 重命名文件或文件夹
		fs.rename(new Path("hdfs://bigdata111:9000/user/itstar/hello.txt"), new Path("hdfs://bigdata111:9000/user/itstar/hellonihao.txt"));
	fs.close();
	}

3.2.7、HDFS文件详情查看

  查看文件名称、权限、长度、块信息

@Test
public void readListFiles() throws Exception {
	// 1 创建配置信息对象
	Configuration configuration = new Configuration();
		
	FileSystem fs = FileSystem.get(new URI("hdfs://bigdata111:9000"),configuration, "itstar");
		
	// 思考:为什么返回迭代器,而不是List之类的容器
	RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);

	while (listFiles.hasNext()) {
		LocatedFileStatus fileStatus = listFiles.next();
			
		System.out.println(fileStatus.getPath().getName());
		System.out.println(fileStatus.getBlockSize());
		System.out.println(fileStatus.getPermission());
		System.out.println(fileStatus.getLen());
			
		BlockLocation[] blockLocations = fileStatus.getBlockLocations();
			
		for (BlockLocation bl : blockLocations) {
				
			System.out.println("block-offset:" + bl.getOffset());
				
			String[] hosts = bl.getHosts();
				
			for (String host : hosts) {
				System.out.println(host);
			}
		}
			
		System.out.println("--------------Andy的分割线--------------");
	}
	}

3.2.8、HDFS文件和文件夹判断

@Test
public void findAtHDFS() throws Exception, IllegalArgumentException, IOException{
		
	// 1 创建配置信息对象
	Configuration configuration = new Configuration();
		
	FileSystem fs = FileSystem.get(new URI("hdfs://bigdata111:9000"),configuration, "itstar");
		
	// 2 获取查询路径下的文件状态信息
	FileStatus[] listStatus = fs.listStatus(new Path("/"));

	// 3 遍历所有文件状态
	for (FileStatus status : listStatus) {
		if (status.isFile()) {
			System.out.println("f--" + status.getPath().getName());
		} else {
			System.out.println("d--" + status.getPath().getName());
		}
	}
}

3.3、通过IO流操作HDFS

3.3.1、HDFS文件上传

	@Test
	public void putFileToHDFS() throws Exception{
		// 1 创建配置信息对象
		Configuration configuration = new Configuration();
		
		FileSystem fs = FileSystem.get(new URI("hdfs://bigdata111:9000"),configuration, "itstar");
		
		// 2 创建输入流
		FileInputStream inStream = new FileInputStream(new File("e:/hello.txt"));
		
		// 3 获取输出路径
		String putFileName = "hdfs://bigdata111:9000/user/itstar/hello1.txt";
		Path writePath = new Path(putFileName);

		// 4 创建输出流
		FSDataOutputStream outStream = fs.create(writePath);

		// 5 流对接
		try{
			IOUtils.copyBytes(inStream, outStream, 4096, false);
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			IOUtils.closeStream(inStream);
			IOUtils.closeStream(outStream);
		}
	}

3.3.2、HDFS文件下载

	@Test
	public void getFileToHDFS() throws Exception{
		// 1 创建配置信息对象
		Configuration configuration = new Configuration();
		
		FileSystem fs = FileSystem.get(new URI("hdfs://bigdata111:9000"),configuration, "itstar");
		
		// 2 获取读取文件路径
		String filename = "hdfs://bigdata111:9000/user/itstar/hello1.txt";
		
		// 3 创建读取path
		Path readPath = new Path(filename);
		
		// 4 创建输入流
		FSDataInputStream inStream = fs.open(readPath);
		
		// 5 流对接输出到控制台
		try{
			IOUtils.copyBytes(inStream, System.out, 4096, false);
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			IOUtils.closeStream(inStream);
		}
	}

3.3.3、定位文件读取

  • 1)下载第一块
@Test
// 定位下载第一块内容
public void readFileSeek1() throws Exception {

	// 1 创建配置信息对象
	Configuration configuration = new Configuration();

	FileSystem fs = FileSystem.get(new URI("hdfs://bigdata111:9000"), configuration, "itstar");

	// 2 获取输入流路径
	Path path = new Path("hdfs://bigdata111:9000/user/itstar/tmp/hadoop-2.7.2.tar.gz");

	// 3 打开输入流
	FSDataInputStream fis = fs.open(path);

	// 4 创建输出流
	FileOutputStream fos = new FileOutputStream("e:/hadoop-2.7.2.tar.gz.part1");

	// 5 流对接
	byte[] buf = new byte[1024];
	for (int i = 0; i < 128 * 1024; i++) {
		fis.read(buf);
		fos.write(buf);
	}

	// 6 关闭流
	IOUtils.closeStream(fis);
	IOUtils.closeStream(fos);
	}
  • 2)下载第二块
	@Test
	// 定位下载第二块内容
	public void readFileSeek2() throws Exception{
		
		// 1 创建配置信息对象
		Configuration configuration = new Configuration();

		FileSystem fs = FileSystem.get(new URI("hdfs://bigdata111:9000"), configuration, "itstar");
		
		// 2 获取输入流路径
		Path path = new Path("hdfs://bigdata111:9000/user/itstar/tmp/hadoop-2.7.2.tar.gz");
		
		// 3 打开输入流
		FSDataInputStream fis = fs.open(path);
		
		// 4 创建输出流
		FileOutputStream fos = new FileOutputStream("e:/hadoop-2.7.2.tar.gz.part2");
		
		// 5 定位偏移量(第二块的首位)
		fis.seek(1024 * 1024 * 128);
		
		// 6 流对接
		IOUtils.copyBytes(fis, fos, 1024);
		
		// 7 关闭流
		IOUtils.closeStream(fis);
		IOUtils.closeStream(fos);
	}
  • 3)合并文件
在window命令窗口中执行
type hadoop-2.7.2.tar.gz.part2 >> hadoop-2.7.2.tar.gz.part1

四、HDFS的数据流

4.1、HDFS写数据流程

4.1.1、剖析文件写入

  • 1)客户端向namenode请求上传文件,namenode检查目标文件是否已存在,父目录是否存在。
  • 2)namenode返回是否可以上传。
  • 3)客户端请求第一个 block上传到哪几个datanode服务器上。
  • 4)namenode返回3个datanode节点,分别为dn1、dn2、dn3。
  • 5)客户端请求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步)

4.1.2、网络拓扑概念

  在本地网络中,两个节点被称为“彼此近邻”是什么意思?在海量数据处理中,其主要限制因素是节点之间数据的传输速率——带宽很稀缺。这里的想法是将两个节点间的带宽作为距离的衡量标准。
  节点距离:两个节点到达最近的共同祖先的距离总和。
  例如,假设有数据中心d1机架r1中的节点n1。该节点可以表示为/d1/r1/n1。利用这种标记,这里给出四种距离描述。
  Distance(/d1/r1/n1, /d1/r1/n1)=0(同一节点上的进程)
  Distance(/d1/r1/n1, /d1/r1/n2)=2(同一机架上的不同节点)
  Distance(/d1/r1/n1, /d1/r3/n2)=4(同一数据中心不同机架上的节点)
  Distance(/d1/r1/n1, /d2/r4/n2)=6(不同数据中心的节点)

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

  • 3)Hadoop2.7.2副本节点选择
      第一个副本在client所处的节点上。如果客户端在集群外,随机选一个。
      第二个副本和第一个副本位于相同机架,随机节点。
      第三个副本位于不同机架,随机节点。

4.2、HDFS读数据流程

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

4.3、一致性模型

  • 1)debug调试如下代码
@Test
	public void writeFile() throws Exception{
		// 1 创建配置信息对象
		Configuration configuration = new Configuration();
		fs = FileSystem.get(configuration);
		
		// 2 创建文件输出流
		Path path = new Path("hdfs://bigdata111:9000/user/itstar/hello.txt");
		FSDataOutputStream fos = fs.create(path);
		
		// 3 写数据
		fos.write("hello".getBytes());
        // 4 一致性刷新
		fos.hflush();
		
		fos.close();
	}
  • 2)总结
      写入数据时,如果希望数据被其他client立即可见,调用如下方法
      FsDataOutputStream. hflush (); //清理客户端缓冲区数据,被其他client立即可见

五、NameNode工作机制

5.1、NameNode&Secondary NameNode工作机制

  • 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

  • 3)web端访问SecondaryNameNode
      (1)启动集群
      (2)浏览器中输入:http://bigdata111:50090/status.html
      (3)查看SecondaryNameNode信息

  • 4)chkpoint检查时间参数设置
      (1)通常情况下,SecondaryNameNode每隔一小时执行一次。
      [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>

5.2、镜像文件和编辑日志文件

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

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

  • 2)oiv查看fsimage文件

(1)查看oiv和oev命令

[itstar@bigdata111 current]$ hdfs
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)案例实操

[itstar@bigdata111 current]$ pwd
/opt/module/hadoop-2.7.2/data/tmp/dfs/name/current

[itstar@bigdata111 current]$ hdfs oiv -p XML -i fsimage_0000000000000000025 -o /opt/module/hadoop-2.7.2/fsimage.xml

[itstar@bigdata111 current]$ cat /opt/module/hadoop-2.7.2/fsimage.xml
将显示的xml文件内容拷贝到IDEA中创建的xml文件中,并格式化。

3)oev查看edits文件

(1)基本语法

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

(2)案例实操

[itstar@bigdata111 current]$ hdfs oev -p XML -i edits_0000000000000000012-0000000000000000013 -o /opt/module/hadoop-2.7.2/edits.xml
[itstar@bigdata111 current]$ cat /opt/module/hadoop-2.7.2/edits.xml
将显示的xml文件内容拷贝到IDEA中创建的xml文件中,并格式化。

5.3、滚动编辑日志

  正常情况HDFS文件系统有更新操作时,就会滚动编辑日志。也可以用命令强制滚动编辑日志。

  • 1)滚动编辑日志(前提必须启动集群)
[itstar@bigdata111 current]$ hdfs dfsadmin -rollEdits
  • 2)镜像文件什么时候产生
Namenode启动时加载镜像文件和编辑日志

5.4、namenode版本号

  • 1)查看namenode版本号
在/opt/module/hadoop-2.7.2/data/tmp/dfs/name/current这个目录下查看VERSION
namespaceID=1933630176
clusterID=CID-1f2bf8d1-5ad2-4202-af1c-6713ab381175
cTime=0
storageType=NAME_NODE
blockpoolID=BP-97847618-192.168.10.102-1493726072779
layoutVersion=-63
  • 2)namenode版本号具体解释

  (1) namespaceID在HDFS上,会有多个Namenode,所以不同Namenode的namespaceID是不同的,分别管理一组blockpoolID。
  (2)clusterID集群id,全局唯一
  (3)cTime属性标记了namenode存储系统的创建时间,对于刚刚格式化的存储系统,这个属性为0;但是在文件系统升级之后,该值会更新到新的时间戳。
  (4)storageType属性说明该存储目录包含的是namenode的数据结构。
  (5)blockpoolID:一个block pool id标识一个block pool,并且是跨集群的全局唯一。当一个新的Namespace被创建的时候(format过程的一部分)会创建并持久化一个唯一ID。在创建过程构建全局唯一的BlockPoolID比人为的配置更可靠一些。NN将BlockPoolID持久化到磁盘中,在后续的启动过程中,会再次load并使用。
  (6)layoutVersion是一个负整数。通常只有HDFS增加新特性时才会更新这个版本号。

5.5、SecondaryNameNode目录结构

  Secondary NameNode用来监控HDFS状态的辅助后台程序,每隔一段时间获取HDFS元数据的快照。
  在/opt/module/hadoop-2.7.2/data/tmp/dfs/namesecondary/current这个目录中查看SecondaryNameNode目录结构。

edits_0000000000000000001-0000000000000000002
fsimage_0000000000000000002
fsimage_0000000000000000002.md5
VERSION

  SecondaryNameNode的namesecondary/current目录和主  namenode的current目录的布局相同。
  好处:在主namenode发生故障时(假设没有及时备份数据),可以从SecondaryNameNode恢复数据。
  方法一:将SecondaryNameNode中数据拷贝到namenode存储数据的目录;
  方法二:使用-importCheckpoint选项启动namenode守护进程,从而将SecondaryNameNode中数据拷贝到namenode目录中。

  • 1)案例实操(一):

  模拟namenode故障,并采用方法一,恢复namenode数据
  (1)kill -9 namenode进程
  (2)删除namenode存储的数据(/opt/module/hadoop-2.7.2/data/tmp/dfs/name)

rm -rf /opt/module/hadoop-2.7.2/data/tmp/dfs/name/*

(3)拷贝SecondaryNameNode中数据到原namenode存储数据目录

cp -R /opt/module/hadoop-2.7.2/data/tmp/dfs/namesecondary/* /opt/module/hadoop-2.7.2/data/tmp/dfs/name/

(4)重新启动namenode

sbin/hadoop-daemon.sh start namenode
  • 2)案例实操(二):

模拟namenode故障,并采用方法二,恢复namenode数据

  (0)修改hdfs-site.xml中的

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

<property>
  <name>dfs.namenode.name.dir</name>
  <value>/opt/module/hadoop-2.7.2/data/tmp/dfs/name</value>
</property>

  (1)kill -9 namenode进程

  (2)删除namenode存储的数据(/opt/module/hadoop-2.7.2/data/tmp/dfs/name)

rm -rf /opt/module/hadoop-2.7.2/data/tmp/dfs/name/*

  (3)如果SecondaryNameNode不和Namenode在一个主机节点上,需要将SecondaryNameNode存储数据的目录拷贝到Namenode存储数据的平级目录。

[itstar@bigdata111 dfs]$ pwd
/opt/module/hadoop-2.7.2/data/tmp/dfs
[itstar@bigdata111 dfs]$ ls
data  name  namesecondary

  (4)导入检查点数据(等待一会ctrl+c结束掉)

bin/hdfs namenode -importCheckpoint

  (5)启动namenode

sbin/hadoop-daemon.sh start namenode

  (6)如果提示文件锁了,可以删除in_use.lock

rm -rf /opt/module/hadoop-2.7.2/data/tmp/dfs/namesecondary/in_use.lock

5.6、集群安全模式操作

  • 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)先进入安全模式
    bin/hdfs dfsadmin -safemode enter
      (2)执行下面的脚本
      编辑一个脚本
#!/bin/bash
bin/hdfs dfsadmin -safemode wait
bin/hdfs dfs -put ~/hello.txt /root/hello.txt
  • 3)再打开一个窗口,执行
bin/hdfs dfsadmin -safemode leave

5.7、Namenode多目录配置

  • 1)namenode的本地目录可以配置成多个,且每个目录存放内容相同,增加了可靠性。
  • 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>

六、DataNode工作机制

6.1、DataNode工作机制

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

6.2、数据完整性

  • 1)当DataNode读取block的时候,它会计算checksum
  • 2)如果计算后的checksum,与block创建时值不一样,说明block已经损坏。
  • 3)client读取其他DataNode上的block.
  • 4)datanode在其文件创建后周期验证checksum

6.3、掉线时限参数设置

  datanode进程死亡或者网络故障造成datanode无法与namenode通信,namenode不会立即把该节点判定为死亡,要经过一段时间,这段时间暂称作超时时长。HDFS默认的超时时长为10分钟+30秒。如果定义超时时间为timeout,则超时时长的计算公式为:

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

  而默认的dfs.namenode.heartbeat.recheck-interval 大小为5分钟,dfs.heartbeat.interval默认为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、DataNode的目录结构

  和namenode不同的是,datanode的存储目录是初始阶段自动创建的,不需要额外格式化。

  • 1)在/opt/module/hadoop-2.7.2/data/tmp/dfs/data/current这个目录下查看版本号
[itstar@bigdata111 current]$ cat VERSION 
storageID=DS-1b998a1d-71a3-43d5-82dc-c0ff3294921b
clusterID=CID-1f2bf8d1-5ad2-4202-af1c-6713ab381175
cTime=0
datanodeUuid=970b2daf-63b8-4e17-a514-d81741392165
storageType=DATA_NODE
layoutVersion=-56
  • 2)具体解释
      (1)storageID:存储id号
      (2)clusterID集群id,全局唯一
      (3)cTime属性标记了datanode存储系统的创建时间,对于刚刚格式化的存储系统,这个属性为0;但是在文件系统升级之后,该值会更新到新的时间戳。
      (4)datanodeUuid:datanode的唯一识别码
      (5)storageType:存储类型
      (6)layoutVersion是一个负整数。通常只有HDFS增加新特性时才会更新这个版本号。

  • 3)在/opt/module/hadoop-2.7.2/data/tmp/dfs/data/current/BP-97847618-192.168.10.102-1493726072779/current这个目录下查看该数据块的版本号

[itstar@bigdata111 current]$ cat VERSION 
#Mon May 08 16:30:19 CST 2017
namespaceID=1933630176
cTime=0
blockpoolID=BP-97847618-192.168.10.102-1493726072779
layoutVersion=-56
  • 4)具体解释

  (1)namespaceID:是datanode首次访问namenode的时候从namenode处获取的storageID对每个datanode来说是唯一的(但对于单个datanode中所有存储目录来说则是相同的),namenode可用这个属性来区分不同datanode。
  (2)cTime属性标记了datanode存储系统的创建时间,对于刚刚格式化的存储系统,这个属性为0;但是在文件系统升级之后,该值会更新到新的时间戳。
  (3)blockpoolID:一个block pool id标识一个block pool,并且是跨集群的全局唯一。当一个新的Namespace被创建的时候(format过程的一部分)会创建并持久化一个唯一ID。在创建过程构建全局唯一的BlockPoolID比人为的配置更可靠一些。NN将BlockPoolID持久化到磁盘中,在后续的启动过程中,会再次load并使用。
  (4)layoutVersion是一个负整数。通常只有HDFS增加新特性时才会更新这个版本号。

6.5、Datanode多目录配置

  • 1)datanode也可以配置成多个目录,每个目录存储的数据不一样。即:数据不是副本。
  • 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>

七、HDFS其他功能

7.1、集群间数据拷贝

  • 1)scp实现两个远程主机之间的文件复制
// 推 push
scp -r hello.txt root@bigdata112:/user/itstar/hello.txt	
// 拉 pull	
scp -r root@bigdata112:/user/itstar/hello.txt  hello.txt
//是通过本地主机中转实现两个远程主机的文件复制;如果在两个远程主机之间ssh没有配置的情况下可以使用该方式。		
scp -r root@bigdata112:/user/itstar/hello.txt root@bigdata113:/user/itstar   
  • 2)采用discp命令实现两个hadoop集群之间的递归数据复制
bin/hadoop distcp hdfs://haoop102:9000/user/itstar/hello.txt hdfs://bigdata112:9000/user/itstar/hello.txt

7.2、Hadoop存档

  • 1)理论概述

  每个文件均按块存储,每个块的元数据存储在namenode的内存中,因此hadoop存储小文件会非常低效。因为大量的小文件会耗尽namenode中的大部分内存。但注意,存储小文件所需要的磁盘容量和存储这些文件原始内容所需要的磁盘空间相比也不会增多。例如,一个1MB的文件以大小为128MB的块存储,使用的是1MB的磁盘空间,而不是128MB。
  Hadoop存档文件或HAR文件,是一个更高效的文件存档工具,它将文件存入HDFS块,在减少namenode内存使用的同时,允许对文件进行透明的访问。具体说来,Hadoop存档文件可以用作MapReduce的输入。

  • 2)案例实操

(1)需要启动yarn进程

start-yarn.sh

(2)归档文件

归档成一个叫做xxx.har的文件夹,该文件夹下有相应的数据文件。Xx.har目录是一个整体,该目录看成是一个归档文件即可。
bin/hadoop archive -archiveName myhar.har -p /user/itstar   /user/my

(3)查看归档

hadoop fs -lsr /user/my/myhar.har
hadoop fs -lsr har:///myhar.har

(4)解归档文件

hadoop fs -cp har:/// user/my/myhar.har /* /user/itstar

7.3、快照管理

  快照相当于对目录做一个备份。并不会立即复制所有文件,而是指向同一个文件。当写入发生时,才会产生新文件。

  • 1)基本语法
(1)hdfs dfsadmin -allowSnapshot 路径   (功能描述:开启指定目录的快照功能)
(2)hdfs dfsadmin -disallowSnapshot 路径 (功能描述:禁用指定目录的快照功能,默认是禁用)
(3)hdfs dfs -createSnapshot 路径        (功能描述:对目录创建快照)
(4)hdfs dfs -createSnapshot 路径 名称   (功能描述:指定名称创建快照)
(5)hdfs dfs -renameSnapshot 路径 旧名称 新名称 (功能描述:重命名快照)
(6)hdfs lsSnapshottableDir         (功能描述:列出当前用户所有可快照目录)
(7)hdfs snapshotDiff 路径1 路径2 (功能描述:比较两个快照目录的不同之处)
(8)hdfs dfs -deleteSnapshot <path> <snapshotName>  (功能描述:删除快照)
  • 2)案例实操

(1)开启/禁用指定目录的快照功能

hdfs dfsadmin -allowSnapshot /user/itstar/data		
hdfs dfsadmin -disallowSnapshot /user/itstar/data	

(2)对目录创建快照

// 对目录创建快照
hdfs dfs -createSnapshot /user/itstar/data	
// 快照和源文件使用相同数据块	
通过web访问hdfs://bigdata111:9000/user/itstar/data/.snapshot/s…..
hdfs dfs -lsr /user/itstar/data/.snapshot/

(3)指定名称创建快照

hdfs dfs -createSnapshot /user/itstar/data miao170508		

(4)重命名快照

hdfs dfs -renameSnapshot /user/itstar/data/ miao170508 itstar170508

(5)列出当前用户所有可快照目录

hdfs lsSnapshottableDir	

(6)比较两个快照目录的不同之处

hdfs snapshotDiff /user/itstar/data/  .  .snapshot/itstar170508	

(7)恢复快照

hdfs dfs -cp /user/itstar/input/.snapshot/s20170708-134303.027 /user

7.4、回收站

  • 1)默认回收站

  默认值fs.trash.interval=0,0表示禁用回收站,可以设置删除文件的存活时间。
  默认值fs.trash.checkpoint.interval=0,检查回收站的间隔时间。
  要求fs.trash.checkpoint.interval<=fs.trash.interval。

  • 2)启用回收站

修改core-site.xml,配置垃圾回收时间为1分钟。

<property>
    <name>fs.trash.interval</name>
    <value>1</value>
</property>
  • 3)查看回收站
回收站在集群中的;路径:/user/itstar/.Trash/….

4)修改访问垃圾回收站用户名称

进入垃圾回收站用户名称,默认是dr.who,修改为itstar用户
[core-site.xml]

<property>
  <name>hadoop.http.staticuser.user</name>
  <value>itstar</value>
</property>
  • 5)通过程序删除的文件不会经过回收站,需要调用moveToTrash()才进入回收站
Trash trash = New Trash(conf);
trash.moveToTrash(path);
  • 6)恢复回收站数据
hadoop fs -mv /user/itstar/.Trash/Current/user/itstar/input    /user/itstar/input
  • 7)清空回收站
hdfs dfs -expunge
原文地址:https://www.cnblogs.com/nthforsth/p/13688337.html