暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

HDFS文件系统

fuzzy maker 2019-06-08
416

详细介绍hdfs shell 操作、api操作、工作流程及各个组件的工作机制

一、HDFS概述

1. HDFS产生背景

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

2. HDFS概念

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

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

3. HDFS 优缺点

3.1 优势

  • 高容错性

    • 数据自动保存多个副本。它通过增加副本的形式,提高容错性。

    • 某一个副本丢失以后,它可以自动恢复,这是由 HDFS 内部机制实现的,我们不必关心。      

  • 适合大数据处理

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

    • 文件规模:能够处理百万规模以上的文件数量,数量相当之大。

  • 流式数据访问

    • 一次写入,多次读取,不能修改,只能追加

    • 它能保证数据的一致性。

  • 可构建在廉价机器上

    • 它通过多副本机制,提高可靠性。

    • 它提供了容错和恢复机制。比如某一个副本丢失,可以通过其它副本来恢复。

3.2 劣势

  • 不适合低延时数据访问

    • 比如毫秒级的来存储数据,这是不行的,它做不到。

    • 它适合高吞吐率的场景,就是在某一时间内写入大量的数据。但是它在低延时的情况下是不行的,比如毫秒级以内读取数据,这样它是很难做到的。

  • 无法高效的对大量小文件进行存储

    • 存储大量小文件的话,它会占用 NameNode大量的内存来存储文件、目录和块信息。这样是不可取的,因为NameNode的内存总是有限的。

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

  • 并发写入、文件随机修改

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

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

4. HDFS架构

主要由四个部分组成,分别为HDFS Client、NameNode、DataNode和Secondary NameNode。下面我们分别介绍这四个组成部分。

4.1 Client

就是客户端

       (1)文件切分。文件上传 HDFS 的时候,Client 将文件切分成一个一个的Block,然后进行存储。

       (2)与NameNode交互,获取文件的位置信息。

       (3)与DataNode交互,读取或者写入数据。

       (4)Client提供一些命令来管理HDFS,比如启动或者关闭HDFS。

       (5)Client可以通过一些命令来访问HDFS。

4.2 NameNode

就是master,它是一个主管、管理者。

       (1)管理HDFS的名称空间

       (2)管理数据块(Block)映射信息

       (3)配置副本策略

       (4)处理客户端读写请求。

4.3 DataNode

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

       (1)存储实际的数据块。

       (2)执行数据块的读/写操作。

4.4 Secondary NameNode

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

       (1)辅助NameNode,分担其工作量。

       (2)定期合并fsimage和fsedits,并推送给NameNode

       (3)在紧急情况下,可辅助恢复NameNode。

5. HDFS 文件块大小

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

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

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

块的大小:10ms * 100 * 100M/s = 100M

二、HFDS命令行操作

1. 基本语法

bin/hadoop fs 具体命令

2. 参数大全

命令参数
[-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> …]:设置hdfs中文件的副本数量
[-stat [format] <path> ...]
[-tail [-f] <file>]
[-test -[defsz] <path>]
[-text [-ignoreCrc] <src> ...]
[-touchz <path> ...]
[-usage [cmd ...]]

三、HDFS客户端操作

1. 通过API操作HDFS

使用api操作hadoop,只需要导入相关依赖即可,不需要本地安装hadoop

1.1 pom文件

<properties>
       <hadoop-version>2.7.2</hadoop-version>
       <hadoop-core-version>1.2.1</hadoop-core-version>
   </properties>

   <dependencyManagement>
       <dependencies>
           <!--hadoop-->
           <dependency>
               <groupId>org.apache.hadoop</groupId>
               <artifactId>hadoop-client</artifactId>
               <version>${hadoop-version}</version>
           </dependency>
           <dependency>
               <groupId>org.apache.hadoop</groupId>
               <artifactId>hadoop-common</artifactId>
               <version>${hadoop-version}</version>
           </dependency>
           <dependency>
               <groupId>org.apache.hadoop</groupId>
               <artifactId>hadoop-hdfs</artifactId>
               <version>${hadoop-version}</version>
           </dependency>
           <dependency>
               <groupId>org.apache.hadoop</groupId>
               <artifactId>hadoop-core</artifactId>
               <version>${hadoop-core-version}</version>
           </dependency>
       </dependencies>
   </dependencyManagement>

1.2 获取HDFS文件系统

常量类

public class HadoopConst {
   public static final String NAMENODE = "hdfs://hadoop101:9000";
   public static final String USERNAME = "root";
}

此种方式创建的 FileSystem 没有指定操作的用户,默认取HADOOP_USER_NAME系统变量,没有当前客户端登陆用户名,需要 添加系统参数-DHADOOP_USER_NAME=root

private final String dirName = "/haha";
@Test
public void test1() throws IOException {
 Configuration conf = new Configuration();
 conf.set("fs.defaultFS",HadoopConst.NAMENODE);
 FileSystem hdfs = FileSystem.get(conf);
 // 创建一个新文件夹
 hdfs.mkdirs(new Path(dirName));
 hdfs.close();
}

1.3 获取HDFS文件系统 pro (升级版)

@Test
public void test2() throws URISyntaxException, IOException, InterruptedException {
 URI uri = new URI(HadoopConst.NAMENODE);
 Configuration conf = new Configuration();
 String user = HadoopConst.USERNAME;
 FileSystem hdfs = FileSystem.get(uri,conf,user);
 hdfs.mkdirs(new Path(dirName));
 hdfs.close();
}


1.4 HDFS 文件上传

private final String srcPath = "/Users/zhukaishengy/IdeaProjects/hadoop-parent/hdfs/src/main/resources/hadoop.yml";
private final String destPath = "/";
@Test
public void test3_1() throws URISyntaxException, IOException, InterruptedException {
 FileSystem hdfs = FileSystem.get(new URI(HadoopConst.NAMENODE),new  Configuration(),HadoopConst.USERNAME);
 Path src = new Path(srcPath);
 Path dest = new Path(destPath);
 hdfs.copyFromLocalFile(src,dest);
 hdfs.close();
 System.out.println("file uploaded");
}


1.5 HDFS配置优先级

在集群的配置中指定副本数为3,在resource中添加hdfs-site.yml文件,设置副本数为2,在代码中指定副本数为1,查看新上传文件的副本数。

@Test
public void test3_2() throws URISyntaxException, IOException, InterruptedException {
   Configuration conf = new Configuration();
   conf.set("dfs.replication","1");
   FileSystem hdfs = FileSystem.get(new URI(HadoopConst.NAMENODE), conf, HadoopConst.USERNAME);
   Path src = new Path(srcPath);
   Path dest = new Path("/");
   hdfs.copyFromLocalFile(src,dest);
   hdfs.close();
   System.out.println("file uploaded");
}

结论:默认集群中的配置 < resource中的配置文件 < 创建Configuration对象时指定


1.6 HDFS文件下载

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

   FileSystem hdfs = FileSystem.get(new URI(HadoopConst.NAMENODE),new Configuration(),HadoopConst.USERNAME);
   Path src = new Path("/hadoop.yml");
   Path dest = new Path("/Users/zhukaishengy/IdeaProjects/hadoop-parent/hdfs/src/main/resources/download");
   hdfs.copyToLocalFile(false,src,dest,true);
   hdfs.close();
   System.out.println("file downloaded");
}


1.7 HDFS删除文件目录

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

   FileSystem hdfs = FileSystem.get(new URI(HadoopConst.NAMENODE),new Configuration(),HadoopConst.USERNAME);
   hdfs.delete(new Path(dirName),true);
   hdfs.close();
   System.out.println("dir deleted");
}


1.8 HDFS修改文件名

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

   FileSystem hdfs = FileSystem.get(new URI(HadoopConst.NAMENODE),new Configuration(),HadoopConst.USERNAME);
   hdfs.rename(new Path("/hadoop.yml"),new Path("/application.yml"));
   hdfs.close();
   System.out.println("file rename");
}


1.9 HDFS文件详情查看

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

   FileSystem hdfs = FileSystem.get(new URI(HadoopConst.NAMENODE),new Configuration(),HadoopConst.USERNAME);
   // 递归获取path下的文件信息
   RemoteIterator<LocatedFileStatus> files = hdfs.listFiles(new Path("/"), true);
   while (files.hasNext()) {
       LocatedFileStatus fileStatus = files.next();
       // 文件名称
       String name = fileStatus.getPath().getName();
       // 权限
       FsPermission permission = fileStatus.getPermission();
       // 长度
       long len = fileStatus.getLen();
       System.out.println(name);
       System.out.println(permission);
       System.out.println(len);
       // 块信息,一个文件对应多个块
       BlockLocation[] blockLocations = fileStatus.getBlockLocations();
       for (BlockLocation blockLocation : blockLocations) {
           // 每个块对应多个主机名称
           String[] hosts = blockLocation.getHosts();
           System.out.println(Arrays.toString(hosts));
      }
  }
   hdfs.close();
   System.out.println("files detail");
}


1.10 HDFS文件和文件夹判断

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

   FileSystem hdfs = FileSystem.get(new URI(HadoopConst.NAMENODE),new Configuration(),HadoopConst.USERNAME);
   Path[] paths = {
           new Path("/usr"),
           new Path("/application.yml")
  };
   FileStatus[] fileStatuses = hdfs.listStatus(paths);
   Arrays.stream(fileStatuses).forEach(fileStatus -> {
       if (fileStatus.isFile()) {
           System.out.println(fileStatus.getPath().getName() + " is a file");
      }
       if (fileStatus.isDirectory()) {
           System.out.println(fileStatus.getPath().getName() + " is a dir");
      }
  });
   hdfs.close();
   System.out.println("file or dir");
}


2. 通过IO流操作HDFS

模拟 FileSystem底层,模仿put、get的api操作

2.1 HDFS文件上传

@Test
public void copyFromLocal() throws URISyntaxException, IOException, InterruptedException {
   // 定义文件目录
   final String srcPath = "/Users/zhukaishengy/IdeaProjects/hadoop-parent/hdfs/src/main/resources/hadoop.yml";
   final String destPath = "/hadoop.yml";
   // 获取fs
   Configuration conf = new Configuration();
   FileSystem fs = FileSystem.get(new URI(HadoopConst.NAMENODE), conf, HadoopConst.USERNAME);
   // 获取输入流
   FileInputStream fileInputStream = new FileInputStream(srcPath);
   // 获取输出流
   FSDataOutputStream outputStream = fs.create(new Path(destPath));
   // 流对倒
   IOUtils.copyBytes(fileInputStream,outputStream,conf,false);
   // 清理客户端缓冲区数据,被其他client立即可见
   outputStream.hflush();
   // 关闭流
   IOUtils.closeStream(outputStream);
   IOUtils.closeStream(fileInputStream);
   fs.close();
}

2.2 HDFS文件下载

@Test
public void copyToLocal() throws URISyntaxException, IOException, InterruptedException {
   // 定义文件目录
   String inputPath = "/hadoop.yml";
   String outputPath = "/Users/zhukaishengy/IdeaProjects/hadoop-parent/hdfs/src/main/resources/hadoop-tmp.yml";
   // 获取fs
   Configuration conf = new Configuration();
   FileSystem fs = FileSystem.get(new URI(HadoopConst.NAMENODE), conf, HadoopConst.USERNAME);
   // 获取输入流
   FSDataInputStream dataInputStream = fs.open(new Path(inputPath));
   // 获取输出流
   FileOutputStream fileOutputStream = new FileOutputStream(outputPath);
   // 流对倒
   IOUtils.copyBytes(dataInputStream, fileOutputStream,conf,true);
   // 关闭流
   IOUtils.closeStream(fileOutputStream);
   IOUtils.closeStream(dataInputStream);
   fs.close();
}

2.3 定位文件读取

文件的分块下载,每块大小128M

  • 下载第一块

@Test
public void copySepOneToLocal() throws URISyntaxException, IOException, InterruptedException {
   // 定义文件目录
   String inputPath = "/usr/local/hadoop-2.7.2.tar.gz";
   String outputPath = "/Users/zhukaishengy/IdeaProjects/hadoop-parent/hdfs/src/main/resources/hadoop.tmp.part01";
   // 获取fs
   Configuration conf = new Configuration();
   FileSystem fs = FileSystem.get(new URI(HadoopConst.NAMENODE), conf, HadoopConst.USERNAME);
   // 获取输入流
   FSDataInputStream dataInputStream = fs.open(new Path(inputPath));
   // 获取输入流
   FileOutputStream fileOutputStream = new FileOutputStream(outputPath);
   // 流对倒
   byte[] buf = new byte[1024];
   for (int i = 0; i < 128 * 1024 ; i++) {
       dataInputStream.read(buf);
       fileOutputStream.write(buf);
  }
   // 关闭流
   IOUtils.closeStream(fileOutputStream);
   IOUtils.closeStream(dataInputStream);
   fs.close();
}
  • 下载剩余的部分

@Test
public void copySepTwoToLocal() throws URISyntaxException, IOException, InterruptedException {
   // 定义文件目录
   String inputPath = "/usr/local/hadoop-2.7.2.tar.gz";
   String outputPath = "/Users/zhukaishengy/IdeaProjects/hadoop-parent/hdfs/src/main/resources/hadoop.tmp.part02";
   // 获取fs
   Configuration conf = new Configuration();
   FileSystem fs = FileSystem.get(new URI(HadoopConst.NAMENODE), conf, HadoopConst.USERNAME);
   // 获取输入流
   FSDataInputStream dataInputStream = fs.open(new Path(inputPath));
   dataInputStream.seek(128 * 1024 * 1024);
   // 获取输出流
   FileOutputStream fileOutputStream = new FileOutputStream(outputPath);
   // 流对倒
   IOUtils.copyBytes(dataInputStream,fileOutputStream,conf,true);
   // 关闭流
   IOUtils.closeStream(fileOutputStream);
   IOUtils.closeStream(dataInputStream);
   fs.close();
}
  • 拼接验证

# 进入工程目录
~ » j hadoop-par__                                                                           zhukaishengy@percent
/Users/zhukaishengy/IdeaProjects/hadoop-parent
------------------------------------------------------------
~/IdeaProjects/hadoop-parent(master*) » cd hdfs/src/main/resources                           zhukaishengy@percent
------------------------------------------------------------
~/IdeaProjects/hadoop-parent/hdfs/src/main/resources(master*) » ll                           zhukaishengy@percent
total 428104
-rw-r--r--  1 zhukaishengy staff          0  4 24 15:45 application.yml
-rw-r--r--  1 zhukaishengy staff  134217728  5 30 12:07 hadoop.tmp.part01
-rw-r--r--  1 zhukaishengy staff   77829046  5 30 12:07 hadoop.tmp.part02
-rw-r--r--  1 zhukaishengy staff         58  4 24 15:45 hadoop.yml
-rw-r--r--  1 zhukaishengy staff        906  4 24 15:45 hdfs-site.xml
-rw-r--r--  1 zhukaishengy staff         19  4 24 15:45 part-r-00000
------------------------------------------------------------
------------------------------------------------------------
# 查看两个部分占用的磁盘空间
~/IdeaProjects/hadoop-parent/hdfs/src/main/resources(master*) » du -ch hadoop.tmp.part01                 zhukaishengy@percent
128M   hadoop.tmp.part01
128M   total
------------------------------------------------------------
~/IdeaProjects/hadoop-parent/hdfs/src/main/resources(master*) » du -ch hadoop.tmp.part02                 zhukaishengy@percent
81M   hadoop.tmp.part02
81M   total
------------------------------------------------------------
# 拼接两个部分
~/IdeaProjects/hadoop-parent/hdfs/src/main/resources(master*) » cat hadoop.tmp.part01 >> hadoop.tar.gz   zhukaishengy@percent
------------------------------------------------------------
~/IdeaProjects/hadoop-parent/hdfs/src/main/resources(master*) » cat hadoop.tmp.part02 >> hadoop.tar.gz   zhukaishengy@percent
------------------------------------------------------------
~/IdeaProjects/hadoop-parent/hdfs/src/main/resources(master*) » du -ch hadoop.tar.gz                     zhukaishengy@percent
208M   hadoop.tar.gz
208M   total
------------------------------------------------------------
# 解压
~/IdeaProjects/hadoop-parent/hdfs/src/main/resources(master*) » tar -zxvf hadoop.tar.gz  
~/IdeaProjects/hadoop-parent/hdfs/src/main/resources(master*) » ll                                       zhukaishengy@percent
total 854088
-rw-r--r--   1 zhukaishengy staff          0  4 24 15:45 application.yml
drwxr-xr-x  12 zhukaishengy staff        384  1 26  2016 hadoop-2.7.2
-rw-r--r--   1 zhukaishengy staff  212046774  5 30 12:16 hadoop.tar.gz
-rw-r--r--   1 zhukaishengy staff  134217728  5 30 12:07 hadoop.tmp.part01
-rw-r--r--   1 zhukaishengy staff   77829046  5 30 12:07 hadoop.tmp.part02
-rw-r--r--   1 zhukaishengy staff         58  4 24 15:45 hadoop.yml
-rw-r--r--   1 zhukaishengy staff        906  4 24 15:45 hdfs-site.xml
-rw-r--r--   1 zhukaishengy staff         19  4 24 15:45 part-r-00000

四、HDFS的数据流

1. HDFS写数据流程

  • 客户端通过Distributed FileSystem模块向namenode请求上传文件,namenode检查目标文件是否已存在,父目录是否存在。


  • namenode返回是否可以上传。

  • 客户端请求第一个 block上传到哪几个datanode服务器上。

  • namenode返回3个datanode节点,分别为dn1、dn2、dn3。

  • 客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。

  • dn1、dn2、dn3逐级应答客户端。

  • 客户端开始往dn1上传第一个block(先从磁盘读取数据放到一个本地内存缓存),以packet为单位,dn1收到一个packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。

  • 当一个block传输完成之后,客户端再次请求namenode上传第二个block的服务器。(重复执行3-7步)。

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(不同数据中心的节点)

3. 机架感知(副本节点选择)

3.1 低版本Hadoop副本节点选择

  • 第一个副本在client所处的节点上。如果客户端在集群外,随机选一个。

  • 第二个副本和第一个副本位于不相同机架的随机节点上。

  • 第三个副本和第二个副本位于相同机架,节点随机。

3.2 Hadoop2.7.2版本副本节点选择

  • 第一个副本在client所处的节点上。如果客户端在集群外,随机选一个。

  • 第二个副本和第一个副本位于相同机架,随机节点。

  • 第三个副本位于不同机架,随机节点。

4. HDFS读数据流程

  • 客户端通过Distributed FileSystem向namenode请求下载文件,namenode通过查询元数据,找到文件块所在的datanode地址。

  • 挑选一台datanode(就近原则,然后随机)服务器,请求读取数据。

  • datanode开始传输数据给客户端(从磁盘里面读取数据放入流,以packet为单位来做校验)。

  • 客户端以packet为单位接收,先在本地缓存,然后写入目标文件。


五、NameNode工作机制

1. NameNode&Secondary NameNode工作机制

  • 第一阶段: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

  • web端访问SecondaryNameNode

(1)启动集群

(2)浏览器中输入:http://hadoop103:50090/status.html,查看SecondaryNameNode信息

  • chkpoint检查时间参数设置

    • 一分钟检查一次操作次数,当操作次数达到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>
    • 通常情况下,SecondaryNameNode每隔一小时执行一次

    • <!-- hdfs-default.xml -->
      <property>
       <name>dfs.namenode.checkpoint.period</name>
       <value>3600</value>
      </property>

2. 镜像文件和编辑日志文件

2.1 基本说明

namenode被格式化之后,将在/opt/module/hadoop-2.7.2/data/tmp/dfs/name/current目录中产生4个文件:fsimage、edits、seen_txid、VERSION

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

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

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

  • 每次Namenode启动的时候都会将fsimage文件读入内存,并从00001开始到seen_txid中记录的数字依次执行每个edits里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成Namenode启动的时候就将fsimage和edits文件进行了合并。

2.2 oiv查看fsimage文件

  • 查看oiv和oev命令

[root@hadoop101 current]# hdfs
oiv                 apply the offline fsimage viewer to an fsimage
oev                 apply the offline edits viewer to an edits file
  • 基本语法

hdfs oiv -p 文件类型 -i镜像文件 -o 转换后文件输出路径
  • 案例实操

    [root@hadoop101 current]# hdfs oiv -p XML -i fsimage_0000000000000000090 -o fs.xml

2.3 oev查看edits文件

  • 基本语法

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

    [root@hadoop101 current]# hdfs oev -p XML -i edits_inprogress_0000000000000000091 -o edits.xml

3. 滚动编辑日志

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

  • 滚动编辑日志(前提必须启动集群)

    [root@hadoop101 current]# hdfs dfsadmin -rollEdits
  • 镜像文件什么时候产生

    Namenode启动时加载镜像文件和编辑日志

4. Namenode版本号

4.1 查看namenode版本号

  • 在/opt/module/hadoop-2.7.2/data/tmp/dfs/name/current这个目录下查看VERSION

namespaceID=1424814699
clusterID=CID-8ba9c24b-cd51-443b-8b74-fec0bbbff3dc
cTime=0
storageType=NAME_NODE
blockpoolID=BP-2063750649-172.16.133.101-1558708194923
layoutVersion=-63

4.2 namenode版本号具体解释

  • namespaceID在HDFS上,会有多个Namenode,所以不同Namenode的namespaceID是不同的,分别管理一组blockpoolID。

  • clusterID集群id,全局唯一

  • cTime属性标记了namenode存储系统的创建时间,对于刚刚格式化的存储系统,这个属性为0;但是在文件系统升级之后,该值会更新到新的时间戳。

  • storageType属性说明该存储目录包含的是namenode的数据结构。

  • blockpoolID:一个block pool id标识一个block pool,并且是跨集群的全局唯一。当一个新的Namespace被创建的时候(format过程的一部分)会创建并持久化一个唯一ID。在创建过程构建全局唯一的BlockPoolID比人为的配置更可靠一些。NN将BlockPoolID持久化到磁盘中,在后续的启动过程中,会再次load并使用。

  • layoutVersion是一个负整数。通常只有HDFS增加新特性时才会更新这个版本号。

5. SecondaryNameNode目录结构

Secondary NameNode用来监控HDFS状态的辅助后台程序,每隔一段时间获取HDFS元数据的快照

5.1 使用SecondaryNameNode恢复namenode中的数据

  • 在/opt/module/hadoop-2.7.2/data/tmp/dfs/namesecondary/current这个目录中查看SecondaryNameNode目录结构,包含fsimage、edits、VERSION

  • SecondaryNameNode的namesecondary/current目录和主namenode的current目录的布局相同。

  • 好处:在主namenode发生故障时(假设没有及时备份数据),可以从SecondaryNameNode恢复数据

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

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

5.2 方式一

  • kill -9 namenode进程

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

    rm -rf opt/module/hadoop-2.7.2/data/tmp/dfs/name/*
  • 拷贝SecondaryNameNode中数据到原namenode存储数据目录

     cp -R opt/module/hadoop-2.7.2/data/tmp/dfs/namesecondary/* opt/module/hadoop-2.7.2/data/tmp/dfs/name/
  • 重新启动namenode

    sbin/hadoop-daemon.sh start namenode

5.3 方式二

  • 修改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>
  • kill -9 namenode进程

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

    rm -rf opt/module/hadoop-2.7.2/data/tmp/dfs/name/*
  • 如果SecondaryNameNode不和Namenode在一个主机节点上,需要将SecondaryNameNode存储数据的目录拷贝到Namenode存储数据的平级目录。

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

    bin/hdfs namenode -importCheckpoint
  • 启动namenode

    sbin/hadoop-daemon.sh start namenode
  • 如果提示文件锁了,可以删除in_use.lock

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

6. 集群安全模式操作

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不会进入安全模式

6.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 (功能描述:等待安全模式状态结束)

6.3 安全模式用法实战

进入安全模式,离开后自动向hdfs集群上传一个文件

  • 先进入安全模式

    [root@hadoop101 hadoop-2.7.2]# bin/hdfs dfsadmin -safemode enter
  • 编写一个脚本,等待安全模式结束自动执行

    # create a shell
    [root@hadoop101 hadoop-2.7.2]# vim leaveSafeModeAuto.sh
    #!/bin/bash
    hdfs dfsadmin -safemode wait
    hadoop fs -put zks.txt
    [root@hadoop101 hadoop-2.7.2]# chmod 777 leaveSafeModeAuto.sh
    # run the shell , terminal is block
    [root@hadoop101 hadoop-2.7.2]# ./leaveSafeModeAuto.sh
  • 离开安全模式

    # open a new terminal and run the shell
    [root@hadoop101 ~]# hdfs dfsadmin -safemode leave
    Safe mode is OFF


7. Namenode多目录配置

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

  • 修改: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工作机制

1. DataNode工作机制


  • 一个数据块在datanode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。

  • DataNode启动后向namenode注册,通过后,周期性(1小时)的向namenode上报所有的块信息。

  • 心跳是每3秒一次,心跳返回结果带有namenode给该datanode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个datanode的心跳,则认为该节点不可用。

  • 集群运行中可以安全加入和退出一些机器


2. 数据完整性

  • 当DataNode读取block的时候,它会计算checksum

  • 如果计算后的checksum,与block创建时值不一样,说明block已经损坏。

  • client读取其他DataNode上的block。

  • datanode在其文件创建后周期验证checksum

  • 校验方式:crc校验

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>


4. DataNode的目录结构

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

4.1 查看版本号

  • 在/opt/module/hadoop-2.7.2/data/tmp/dfs/data/current 目录下查看版本号

    [root@hadoop101 current]# pwd
    /opt/module/hadoop-2.7.2/data/tmp/dfs/data/current
    [root@hadoop101 current]# ll
    total 4
    drwx------. 4 root root  51 May 27 12:18 BP-2063750649-172.16.133.101-1558708194923
    -rw-r--r--. 1 root root 229 May 27 12:18 VERSION
    [root@hadoop101 current]# cat VERSION
    #Mon May 27 12:18:09 HKT 2019
    storageID=DS-5e6f8358-d37c-45f4-9686-59f7092f2992
    clusterID=CID-8ba9c24b-cd51-443b-8b74-fec0bbbff3dc
    cTime=0
    datanodeUuid=9b61380b-cfaf-4abc-a628-2d62b121cdac
    storageType=DATA_NODE
    layoutVersion=-56
  • 具体解释

    • storageID:存储id号

    • clusterID集群id,全局唯一

    • cTime属性标记了datanode存储系统的创建时间,对于刚刚格式化的存储系统,这个属性为0;但是在文件系统升级之后,该值会更新到新的时间戳。

    • datanodeUuid:datanode的唯一识别码

    • storageType:存储类型

    • layoutVersion是一个负整数。通常只有HDFS增加新特性时才会更新这个版本号。

4.2 查看数据块的版本号

  • 在/opt/module/hadoop-2.7.2/data/tmp/dfs/data/current/BP-2063750649-172.16.133.101-1558708194923/current 目录下查看该数据块的版本号

    [root@hadoop101 current]# pwd
    /opt/module/hadoop-2.7.2/data/tmp/dfs/data/current/BP-2063750649-172.16.133.101-1558708194923/current
    [root@hadoop101 current]# ll
    total 4
    drwxr-xr-x. 3 root root  20 May 24 22:52 finalized
    drwxr-xr-x. 2 root root   6 May 27 16:33 rbw
    -rw-r--r--. 1 root root 134 May 27 12:18 VERSION
    [root@hadoop101 current]# cat VERSION
    #Mon May 27 12:18:10 HKT 2019
    namespaceID=1424814699
    cTime=0
    blockpoolID=BP-2063750649-172.16.133.101-1558708194923
    layoutVersion=-56
  • 具体解释

    • namespaceID:是datanode首次访问namenode的时候从namenode处获取的storageID对每个datanode来说是唯一的(但对于单个datanode中所有存储目录来说则是相同的),namenode可用这个属性来区分不同datanode。

    • cTime属性标记了datanode存储系统的创建时间,对于刚刚格式化的存储系统,这个属性为0;但是在文件系统升级之后,该值会更新到新的时间戳。

    • blockpoolID:一个block pool id标识一个block pool,并且是跨集群的全局唯一。当一个新的Namespace被创建的时候(format过程的一部分)会创建并持久化一个唯一ID。在创建过程构建全局唯一的BlockPoolID比人为的配置更可靠一些。NN将BlockPoolID持久化到磁盘中,在后续的启动过程中,会再次load并使用。

    • ayoutVersion是一个负整数。通常只有HDFS增加新特性时才会更新这个版本号。

5. 服役新数据节点

随着公司业务的增长,数据量越来越大,原有的数据节点的容量已经不能满足存储数据的需求,需要在原有集群基础上动态添加新的数据节点。

示例说明,根据hadoop100克隆hadoop104,希望添加到上面的集群中

5.1 环境准备

  • 克隆一台虚拟机,hadoop104

  • 修改ip地址和主机名称

  • 配置免密登陆

  • 删除原来HDFS文件系统留存的文件:/opt/module/hadoop-2.7.2/data

5.2 操作流程

  • 在namenode所在节点创建dfs.hosts文件

    [root@hadoop101 hadoop]# pwd
    /opt/module/hadoop-2.7.2/etc/hadoop
    [root@hadoop101 hadoop]# vim dfs.hosts
    hadoop101
    hadoop102
    hadoop103
    hadoop104
  • 在namenode的hdfs-site.xml配置文件中增加dfs.hosts属性

    <property>
    <name>dfs.hosts</name>
     <value>/opt/module/hadoop-2.7.2/etc/hadoop/dfs.hosts</value>
    </property>
  • 更新hadoop104的hadoop etc配置,使节点的datanode可以正常启动

    [root@hadoop101 hadoop-2.7.2]# scp -r etc/hadoop/* root@hadoop104:/opt/module/hadoop-2.7.2/etc/hadoop/
  • 刷新namenode

    [root@hadoop101 hadoop]# hdfs dfsadmin -refreshNodes
    Refresh nodes successful
  • 更新resourcemanager节点

    [root@hadoop101 hadoop]# yarn rmadmin -refreshNodes
    19/05/27 17:38:39 INFO client.RMProxy: Connecting to ResourceManager at hadoop102/172.16.133.102:8033
  • 单独命令启动新的数据节点和节点管理器

    [root@hadoop100 hadoop-2.7.2]# sbin/hadoop-daemon.sh start datanode
    starting datanode, logging to opt/module/hadoop-2.7.2/logs/hadoop-root-datanode-hadoop100.out
    [root@hadoop100 hadoop-2.7.2]# sbin/yarn-daemon.sh start nodemanager
    starting nodemanager, logging to opt/module/hadoop-2.7.2/logs/yarn-root-nodemanager-hadoop100.out
  • 在web浏览器上检查是否ok

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

    [root@hadoop101 hadoop-2.7.2]# ./sbin/start-balancer.sh 


6.退役旧数据节点

  • 在namenode的/opt/module/hadoop-2.7.2/etc/hadoop目录下创建dfs.hosts.exclude文件

    [root@hadoop101 hadoop]# pwd
    /opt/module/hadoop-2.7.2/etc/hadoop
    [root@hadoop101 hadoop]# vim dfs.hosts.exclude
    hadoop104
  • 在namenode的hdfs-site.xml配置文件中增加dfs.hosts.exclude属性

    <property>
     <name>dfs.hosts.exclude</name>
     <value>/opt/module/hadoop-2.7.2/etc/hadoop/dfs.hosts.exclude</value>
    </property>
  • 刷新namenode、刷新resourcemanager

    [root@hadoop101 hadoop]# hdfs dfsadmin -refreshNodes
    Refresh nodes successful
    [root@hadoop101 hadoop]# yarn rmadmin -refreshNodes
    19/05/27 19:06:45 INFO client.RMProxy: Connecting to ResourceManager at hadoop102/172.16.133.102:8033
  • 检查web浏览器,退役节点的状态为decommission in progress(退役中),说明数据节点正在复制块到其他节点

  • 等待退役节点状态为decommissioned(所有块已经复制完成),停止该节点及节点资源管理器。注意:如果副本数是3,服役的节点小于等于3,是不能退役成功的,需要修改副本数后才能退役。

  • 从include文件中删除退役节点,再运行刷新节点的命令

    # remove hadoop104
    [root@hadoop101 hadoop]# vim dfs.hosts
    hadoop101
    hadoop102
    hadoop103
    # refresh
    [root@hadoop101 hadoop]# hdfs dfsadmin -refreshNodes
    Refresh nodes successful
    [root@hadoop101 hadoop]# yarn rmadmin -refreshNodes
    19/05/27 19:10:22 INFO client.RMProxy: Connecting to ResourceManager at hadoop102/172.16.133.102:8033
  • 如果数据不均衡,可以用命令实现集群的再平衡

    [root@hadoop101 hadoop-2.7.2]# ./sbin/start-balancer.sh


7. Datanode多目录配置

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

  • 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其他功能

1. hadoop集群间数据拷贝

  • 采用discp命令实现两个hadoop集群之间的递归数据复制

    # 这是一个例子,应该是两个集群
    [root@hadoop101 hadoop-2.7.2]# hadoop distcp hdfs://hadoop101:9000/hadoop.yml hdfs://hadoop102:9000/hadoop.yml

2. Hadoop存档

1)理论概述

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

Hadoop存档文件或HAR文件,是一个更高效的文件存档工具,它将文件存入HDFS块,在减少namenode内存使用的同时,允许对文件进行透明的访问。具体说来,Hadoop存档文件可以用作MapReduce的输入。

2)案例实操

(1)归档文件

归档成一个叫做xxx.har的文件夹,该文件夹下有相应的数据文件。Xx.har目录是一个整体,该目录看成是一个归档文件即可。本质是一个mr。

  • 用法

    hadoop archive -archiveName <NAME>.har -p <parent path> [-r <replication factor>]<src>* <dest>
  • 示例:演示将/usr/local/*归档为har文件。

hadoop archive -archiveName my.har -p usr/local 

(2)查看归档

  • 方式一

    [root@hadoop101 hadoop-2.7.2]# hadoop fs -lsr my.har
    lsr: DEPRECATED: Please use 'ls -R' instead.
    -rw-r--r--   3 root supergroup          0 2019-05-27 19:39 /my.har/_SUCCESS
    -rw-r--r--   5 root supergroup        227 2019-05-27 19:39 /my.har/_index
    -rw-r--r--   5 root supergroup         22 2019-05-27 19:39 /my.har/_masterindex
    -rw-r--r--   3 root supergroup  212046779 2019-05-27 19:39 /my.har/part-0


  • 方式二

    [root@hadoop101 hadoop-2.7.2]# hadoop fs -lsr har:///my.har
    lsr: DEPRECATED: Please use 'ls -R' instead.
    -rw-r--r--   3 root supergroup  212046774 2019-05-24 23:03 har:///my.har/hadoop-2.7.2.tar.gz
    -rw-r--r--   3 root supergroup          5 2019-05-24 22:52 har:///my.har/zks.txt


(4)解归档文件

[root@hadoop101 hadoop-2.7.2]# hadoop fs -cp har:///my.har/* /





文章转载自fuzzy maker,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论