Hadoop(6) HDFS


Hadoop(6)
HDFS

文章目录

    • Hadoop(6) HDFS
      • HDFS
      • HDFS介绍
        • HDFS的定义
        • HDFS的优缺点
        • HDFS的应用场景
      • HDFS的组成
      • 配置Hadoop开发环境
      • HDFS的Shell命令
        • HDFS操作
        • HDFS上传操作
        • HDFS下载操作
      • HDFS的API操作
        • 开发软件中开发环境的搭建
        • API测试
      • HDFS上传和下载的流程
        • 客户端上传文件到HDFS流程(写流程)
        • 从HDFS下载文件的流程(读流程)
      • 元数据
        • 元数据介绍
        • 元数据存储的内容
        • 元数据的合并流程
        • 查看元数据内容
      • DataNode
        • DataNode的工作流程
        • DataNode多目录配置
      • 黑名单和白名单
        • 黑名单
        • 白名单Decommission In Progress
      • 节点的扩展和退役
        • 节点扩展
        • 节点退役
      • HDFS小文件处理
      • HDFS小文件处理

HDFS


HDFS介绍

HDFS的定义

HDFS(Hadoop Distributed File System),即Hadoop分布式文件系统

HDFS由多台主机搭建服务器集群来实现,集群中的服务器分别负责不同的功能

HDFS通过目录树来定位文件

HDFS的优缺点

优点

  1. 采用多副本机制,如果一个副本丢失,可以自动恢复,容错性高
  2. 适合处理大数据
  3. 可以构建在廉价的机器上,随时航向扩展,通过多副本机制提高可靠性

缺点

  1. 不适合低延迟访问数据
  2. 无法高效的对大量小文件进行存储
    1. 存储大量的小文件会占用大量的NameNode内存
    2. 小文件的寻址时间会浪费大量的时间,从而降低效率(一般来说寻址时间<传输时间的 1%比较合适)
  3. 不支持并发写入和文件的修改
    1. 一个文件只能单线程写入,不允许多线程操作
    2. 仅支持数据的追加,不支持文件的修改
HDFS的应用场景

HDFS适合一次写入,多次读取,并且不支持文件的修改,适合做数据分析


HDFS的组成

Hadoop(6) HDFS_第1张图片

HDFS由NameNode,DataNode,ClientSecondaryNameNode组成,在
HDFS中,文件以切块的形式存在,即一个大文件分成多个部分来存储,每个块大小为128MB(1.x版本中是64MB)

一个块(block)如果太小,会增加寻址时间,如果一个block太大,会导致MapReduce处理这块数据时会非常慢

注意

HDFS的块大小主要取决与磁盘的传输速率

  • NameNode 可以理解为结群的主管
    • 管理HDFS的名称空间
    • 配置副本策略
    • 管理数据块的信息(不是数据块)
    • 处理客户端的读写请求(只是处理请求,不处理读写过程)
  • DataNode 负责具体的任务执行
    • 存储实际的数据块
    • 执行数据块的读写操作
  • Client 客户端
    • 与NameNode交互,获取数据块信息
    • 与DataNode交互,负责向DataNode读写数据
    • 上传文件时负责文件的切块
    • 通过指令管理和操作HDFS
  • .SecondaryNameNode
    • 只是对NameNode起辅助作用,注意不是NameNode的热备


配置Hadoop开发环境

由于加使用的操作系统,开发软件可能都不一样,就比如我使用的是基于Linux的Deepin15.11桌面版,开发软件使用的是idea,相比大多数人使用的是Windows系统下Eclipse开发,所以操作不太一样

虽然操作不同,但是配置的思路给大家列举一下,如果大家不会配置,就根据下面的步骤百度一下:

  1. 在开发软件中配置Maven
  2. 将Hadoop的软件包(zip或者tar.gz)解压到自己的磁盘中,不能有中文路径
  3. 配置Hadoop的环境变量
  4. 配置完之后重启一下电脑

HDFS的Shell命令

注意

1. 在执行命令时,使用bin/hadoop fsbin/hdfs dfs的效果是一样的,这是因为dfs是fs的实现类,它们底层调用的jar包都是一样的

2. HDFS的命令和Linux的命令很像,如果熟悉Linux指令,HDFS的Shell操作可以很快上手

3. 下面所有的命令演示,涉及到HDFS文件(夹)的变更,都可以通过浏览器访问NameNode50070端口查看HDFS的文件系统详情来查看命令执行的结果

  • 查看fs的所有命令
hadoop fs

也可以通过下面的指令查看

hadoop fs --help

注意

1. 同样的hadoop dfshadoop dfs --help也一样,下面我们就不把fsdfs一一做列举了,我们统一使用fs

2. 通过以上指令可以查看HDFS的所有指令,下面我们只列举最常用的几个指令,如果大家感兴趣可以把HDFS所有的命令自己测试一遍

HDFS操作
  • 查看指定目录的内容
hadoop fs -ls <目录路径>
  • 查看文本文件内容
hadoop fs -cat <文件路径>
  • 复制
hadoop fs -cp <文件(夹)路径> <拷贝后的文件夹路径>
  • 删除
hadoop fs -rm -r <要删除的文件夹路径>
  • 查看分区情况
Hadoop fs -df [选项]

选项:

选项 说明
-h 存储单位以比较友好的方式展现,说白了就是把单位转换成GB,MB等
  • 查看文件夹的使用情况
hadoop fs -du [选项] <路径>

选项:

选项 说明
-h 同df,存储单位以比较友好的方式展现,说白了就是把单位转换成GB,MB等
  • 设置文件副本数量
hadoop fs -setrep <副本数量n> 

注意

设置的数量一定要小于DataNode的数量,否则只能创建DataNode个数的副本

HDFS上传操作
  • 上传文件到HDFS(复制本地文件到HDFS)
hadoop fs -copyFromLocal <本地文件路径> 

或者

hadoop fs -put <本地文件路径>  

注意

-copyFromLocal-put的作用是一样的,在底层copyFromLocal其实是put的一个子类,但是这个子类什么其他的功能都没有实现,只是换了一个名字而已

  • 剪切本地的文件到HDFS

其实moveFromLocalcopyFromLocal唯一的区别就是一个是剪切,一个是复制

hadoop fs -moveFromLocal <本地文件路径> 
  • 追加内容到HDFS中的文件中
hadoop -appendToFile <本地文件路径> <要追加到的文件路径>
HDFS下载操作
  • 下载文件(复制HDFS文件到本地)

下载同上传一样,有两个方法

hadoop fs -copyToLocal  <下载到本地的文件路径>

或者

hadoop fs -get  <下载到本地的文件路径>
  • 下载多个文件到本地,并合并成一个文件
hadoop fs -getmerge   ...  <本地合并后的文件路径>

HDFS的API操作

开发软件中开发环境的搭建
  1. 首先,新建一个Maven项目(不需要官方模板,直接新建简单项目即可)

  2. 然后引入依赖

<dependencies>
		<dependency>
			<groupId>junitgroupId>
			<artifactId>junitartifactId>
			<version>RELEASEversion>
		dependency>
		<dependency>
			<groupId>org.apache.logging.log4jgroupId>
			<artifactId>log4j-coreartifactId>
			<version>2.8.2version>
		dependency>
		<dependency>
			<groupId>org.apache.hadoopgroupId>
			<artifactId>hadoop-commonartifactId>
			<version>2.7.7version>
		dependency>
		<dependency>
			<groupId>org.apache.hadoopgroupId>
			<artifactId>hadoop-clientartifactId>
			<version>2.7.7version>
		dependency>
		<dependency>
			<groupId>org.apache.hadoopgroupId>
			<artifactId>hadoop-hdfsartifactId>
			<version>2.7.7version>
		dependency>
		<dependency>
			<groupId>jdk.toolsgroupId>
			<artifactId>jdk.toolsartifactId>
			<version>1.8version>
			<scope>systemscope>
			<systemPath>${JAVA_HOME}/lib/tools.jarsystemPath>
		dependency>
dependencies>
  1. 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
API测试
  1. 上传一个文件到HDFS
//注意:导入的包都是org.apache.hadoop中的
@Test
public void put() throws IOException, InterruptedException {
	//获取文件系统,参数: ①URI,②配置(其实就是获取了Hadoop中的core-site.xml文件中的信息),③云主机中的用户名
    FileSystem fileSystem = FileSystem.get(URI.create("hdfs://:9000"),new Configuration(),"");
    //上传文件, 参数: ①本地的文件路径,②Hadoop中存储的路径
    fileSystem.copyFromLocalFile(new Path("<本机上的文件路径>"), new Path("/"));
    //关流
    fileSystem.close();
}

如果执行结果返回的是0,说明上传成功

  1. 下载也是一样
@Test
public void put() throws IOException, InterruptedException {
	//获取文件系统,参数: ①URI,②配置(其实就是获取了Hadoop中的core-site.xml文件中的信息),③云主机中的用户名
    FileSystem fileSystem = FileSystem.get(URI.create("hdfs://:9000"),new Configuration(),"");
    //上传文件, 参数: ①本地的文件路径,②Hadoop中存储的路径
    fileSystem.copyFromLocalFile(new Path("/1.txt"),new Path("<本机上的文件路径>"));
    //关流
    fileSystem.close();
}
  1. 重命名
@Test
public void rename() throws IOException, InterruptedException {
    FileSystem fileSystem = FileSystem.get(URI.create("hdfs://:9000"), new Configuration(), "");
    fileSystem.rename(new Path("<要重命名的文件路径>"), new Path("<重命名之后文件路径>"));
    fileSystem.close();
}
  1. 删除
@Test
public void del() throws IOException, InterruptedException {
    FileSystem fileSystem = FileSystem.get(URI.create("hdfs://:9000"), new Configuration(), "");
    //delete方法返回一个boolean值表示是否删除成功, 参数: ①HDFS中的文件路径,②是否递归删除
    boolean delete = fileSystem.delete(new Path(""), true);
    if (delete) {
    	System.out.println("删除成功");
    } else {
    	System.out.println("删除失败");
    }
    fileSystem.close();
}
  1. 查看文件(夹)信息
@Test
public void read() throws IOException, InterruptedException {
    FileSystem fileSystem = FileSystem.get(URI.create("hdfs://:9000"), new Configuration(), "");
    FileStatus[] fileStatuses = fileSystem.listStatus(new Path("/"));
    for (FileStatus fileStatus:fileStatuses) {
        //判断是否是一个文件
            if (fileStatus.isFile()) {
                //获取文件路径
                System.out.print("文件路径"+fileStatus.getPath()+"\t");
                //获取文件长度
                System.out.println("文件长度"+fileStatus.getLen());
            } else {
                System.out.println("这是一个文件夹,路径:"+fileStatus.getPath());
            }
    }
}

总之可以获取各种各样的文件信息,输入fileStatus.,后面会自动提示很多方法,大家可以自己去试一下,这里就不一一举例了

除此之外,如果只是想获取指定HDFS目录下的文件内容(可以选择是否递归),可以使用listFiles方法,该方法返回一个迭代器,可以通过hadNext方法判断是否有下一个文件,然后处理每个文件,使用listFiles还可以查看每个文件所在块的信息

@Test
public void getFiles() throws IOException, InterruptedException {
    FileSystem fileSystem = FileSystem.get(URI.create("hdfs://192.168.8.101:9000"), new Configuration(), "alasky");
    //创建一个迭代器,接收HDFS指定目录下的所有文件,参数:①HDFS文件路径,②是否递归
    RemoteIterator<LocatedFileStatus> locatedFileStatusRemoteIterator = fileSystem.listFiles(new Path("/"), true);
    //查看迭代器中是否还有下一个内容
    while (locatedFileStatusRemoteIterator.hasNext()) {
        System.out.print("文件信息: ");
        //接收迭代器中的当前文件
        LocatedFileStatus locatedFileStatus = locatedFileStatusRemoteIterator.next();
        System.out.println(locatedFileStatus.getPath());
        System.out.println("块信息:");
        //创建一个BlockLocation数组,接收这给文件存储的块信息
        BlockLocation[] blockLocations=locatedFileStatus.getBlockLocations();
        for (BlockLocation blockLocation : blockLocations) {
            //获取每个块所在的主机
            String[] hosts = blockLocation.getHosts();
            for (String host : hosts) {
            	System.out.println(host);
            }
        }
    }
    fileSystem.close();
}

说明 为什么使用listStatus()方法获取的FileStatus没有块信息,而使用listFiles()方法获取的BlockLocation有块信息?

因为使用listStatus获取的内容不止有文件,还有文件夹(目录),但是目录并没有块(block)的概念,只有文件才有,如果获取文件夹的块信息会出错,所以FileStatus并没有获取块信息的方法

这里说明一下,其实空文件也没有占用block的,但是Hadoop的API里面默认所有的文件都有block


HDFS上传和下载的流程

客户端上传文件到HDFS流程(写流程)

Hadoop(6) HDFS_第2张图片

  • 成功上传的流程
  1. 首先Client(客户端)读取要上传的文件
  2. 然后Client获取HDFS集群的信息(Distributed File System)
  3. Client向NameNode发起上传文件的请求
  4. NameNode检查该请求是否合法,如是否存在相同路径的文件,客户端是否有上传文件的权限,然后将检查结果返回给Client
  5. Client收到NameNode的允许上传的响应之后,将要上传的文件进行逻辑切分(不是真正的切分),如果文件不需要切分,那就不切分
  6. Client开启一个输出流(FSDataOutputStream),请求上传第一个Block(块),然后NameNode返回给Client一个DataNode的List,即客户端要把该Block上传到这几个DataNode中,设置的副本有几个,List中的DataNode个数就有几个
  7. 然后Client向List中的第一个DataNode发送建立通道的请求,第一个收到请求之后再向第二个DataNode发送请求,直到List的最后一个DataNode收到请求,然后返回响应给上一个DataNode,直到返回响应给第一个DataNode,最后返回响应给Client(即串联请求和响应)
  8. 然后Client开始向LIst中的第一个DataNode发送Packet,即第一个切块的数据,每个Packet大小64kb
  9. List中的第一个DataNode收到Packet后,一边向磁盘中写,一边将该Packet发给第2个DataNode,以此类推,直到最后一个DataNode写入磁盘成功,然后再串联响应到Client.注意Packet不是一个一个的发送,而是一次发送一个队列的Packet以提高效率
  10. 第1个Block发送成功之后,再发送第2个Block,(从第6步开始),注意每次上传Block的DataNode可能不一样,直到最后一个Block传输完毕,然后Client发送通知给NameNode,告知NameNode数据已经传输完毕
  11. NameNode收到传输完毕的数据之后,将本次传输记录到元数据中,然后断开数据流的连接
  • 出现问题的解决
  1. 如果在Client和DataNode建立通道的过程出现问题,那么本次上传就失败
  2. 如果在Client向List中第一个DataNode传输数据的过程中出现问题,那么本次上传失败
  3. 如果List中第一个DataNode上传成功但是在后续的备份过程中失败(即第一个DataNode向第二个DataNode传输失败),那么文件上传会继续,但是在HDFS集群中会触发备份,从而将第一个DataNode中的Block备份到其他DataNode

说明

  1. NameNode是通过 **机架感知策略 **选取返回给Client的DataNode列表的,介绍如下:
    1. NameNode会选取离Client最近的一个DataNode(不是距离最近,而是在网络拓扑中最近),如果有多个DataNode距离Client的网络拓扑距离都一样近,那就随机选取一个,然后把选中的这个DataNode作为List的第一个
    2. 选出节点中第一个DataNode之后,第二个DataNode选取相同机架上的其他一个节点
    3. 第三个DataNode选取不同机架上的一个节点
从HDFS下载文件的流程(读流程)

Hadoop(6) HDFS_第3张图片

  • 成功下载的流程
  1. Client获取HDFS集群的信息(Distributed File System)
  2. Client向NameNode发起下载文件的请求
  3. NameNode检查该请求是否合法,如HDFS中是否存在该文件路径,Client是否有读的权限等,然后将结果返回给Client
  4. Client收到NameNode的允许下载的响应之后,Client开启一个输入流(FSDataInputStream),然后NameNode返回存储该文件第一个Block副本的几个DataNode列表给Client
  5. Client向列表中的第一个DataNode发送建立通道的请求,然后下载文件,下载完第一个Block之后,Client再向NameNode请求下载第二个,直到下载完成
  6. 下载完成之后Client会通知NameNode下载完成,然后NameNode关闭流的连接
  • 出现问题的解决
  1. 如果Client向List中的第一个DataNode建立请求失败,那么就会再向第二个发送建立连接的请求,如果List中的所有DataNode全部建立连接失败,那么本次下载就失败

元数据

元数据介绍

在HDFS中的每个块的信息,都会被NameNode记录到元数据中,而且上面说过的读写操作,Client都会先从NameNode获取块的信息,这些信息就存储在元数据当中.

元数据会分别存储在内存和磁盘中,存在内存中的元数据是为了快速读取信息,而存储在磁盘中是为了备份,方便恢复

在磁盘中的元数据是以fsimage的文件存在,但是如果每次操作都在fsimage中更新,就会极大的降低效率,但是如果隔一段时间才更新一次,如果更新之前NameNode出现宕机,那么最后一次更新之后的元数据有没法保存,所以NameNode除了fsimage之外,还会将每次增删改操作以日志的形式记录在edits文件中,edits文件只做追加操作(append),不做修改,所以效率很高,然后每隔一段时间,就将edits文件中的新内容合并到fsimage中

如果出现宕机,NameNode会结合fsimage文件和edits文件来获取到全部的元数据,然后重新加载到内存中,以保证元数据的完整性

总结一下,元数据会存储在内存中和磁盘中,磁盘中的元数据存在fsimage文件里,每次写操作都会记录日志,存在edits文件里

注意

由于fsimage文件每隔一段时间才会更新一次,所以fsimage文件中的元数据和内存中的元数据不是实时同步的

元数据存储的内容
  • 数据块的虚拟存储路径(注意不是真实路径)
  • 文件的切块数量
  • 数据块的复本数量
  • 文件权限

说明

可以看到fsimage中没有存储数据块的实际物理存储路径,这是因为在HDFS启动之后,DataNode每隔一段时间(默认1h)之后会向NameNode上报该DataNode存储的块信息,然后由NameNode加载到内存中

元数据的合并流程

Hadoop(6) HDFS_第4张图片

  1. 第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存
  2. Client(客户端) 发送写请求,当NameNode接收到写请求之后,会先给这次操作分配一个全局事务id将该请求记录到edits_inprogess文件中,如果记录成功,则将该请求同步更新到内存中,修改内存中的元数据,内存修改完成之后会给客户端返回一个ack表示成功
  3. 在触发一定的条件之后,便会将新的元数据更新到fsimage文件中,条件如下,满足其中的一个条件即可触发fsimage的更新
  • 空间阈值:当edits_inprogress文件达到指定大小的时候就会触发更新(默认是64M),
    大小可以由core-site.xml配置文件中的fs.checkpoint.size属性来指定,默认单位是字节
  • 时间阈值:当距离上一次更新达到指定间隔(默认是1h)时候的时候就会触发更新,大小
    可以由fs.checkpoint.period来指定,默认单位是秒
  • 重启更新:NameNode重启之后,会自动的将edits_inprogress中的操作更新到
    fsimage中
  • 强制更新:使用hadoop dfsadmin -rollEdits命令来手动更新
  1. 更新fsimage时,如果集群中存在.SecondaryNameNode节点,就会将edits和fsimage的合并工作在.SecondaryNameNode中进行,如果没有,就会在NameNode本地进行

    在更新的时候,会将edits_inprogress重命名为edits_XXX-XXX(XXX-XXX是这个edits文件所记录的全局事务id的范围),同时产生一个新的edits_inprogress,然后将重命名之后的edits文件和fsimage文件发送到.SecondaryNameNode中,由SecondaryNameNode进行合并

  2. SecondaryNameNode合并完成之后,将新的fsimage文件发回到NameNode

注意

为了数据的可靠性和完整性,在NameNode中会保存2个fsimage文件,一个是最新的fsimage文件,一个是上一个fsimage文件,当SecondaryNameNode将新的fsimage文件发送过来时,NameNode会自动删除最旧的那个fsimage文件

查看元数据内容
  • 查看fsimage
hdfs oiv -p <文件格式> -i  -o <转换文件格式之后输出到的路径>

然后查看输出的文件即可

一般将fsimage转换为xml格式文件

举例

hdfs oiv -p XML -i fsimage_0000000000000000025 -o /opt/hadoop-2.7.7/fsimage.xml
  • 查看edits
hdfs oev -p <文件格式> -i  -o <转换文件格式之后输出到的路径>

DataNode

DataNode的工作流程

Hadoop(6) HDFS_第5张图片

  1. 一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳
  2. DataNode启动之后,如果是第一次加入集群,会向NameNode注册,通过注册后,DataNode每隔一小时就向NameNode上报该DataNode上所有块的信息
  3. DataNode每隔3秒向NameNode发送一次心跳,以通知NameNode自己还存活,如果NameNode超过10分钟没有收到某个DataNode的心跳,就任务该DataNode宕机,就会把该节点上的数据再备份一份到其他节点
DataNode多目录配置

如果有一天我们在某个DataNode节点上新添加了一块硬盘,想要也将数据存储到这块硬盘中,那么可以再从hdfs-site.xml配置文件中添加多个DataNode的存储路径,配置如下


    dfs.datanode.data.dir
	file:///${hadoop.tmp.dir}<路径1>,file:///${hadoop.tmp.dir}<路径2>


黑名单和白名单

黑名单

介绍

顾名思义,黑名单就是不允许指定的DataNode接入集群

配置

  1. 首先创建一个文件存储黑名单的文件(名称可以自定义),然后在里面写上黑名单的地址,直接把地址写上就可以
  2. 然后在hdfs-site.xml配置文件中增加dfs.hosts.exclude属性

	dfs.hosts.exclude
    <黑名单文件路径>

  1. 刷新NameNode和ResourceManager
hdfs dfsadmin -refreshNodes
yarn rmadmin -refreshNodes

注意

配置了黑名单的DataNode节点,仍旧可以连接集群,只不过是NameNode不在为其分配数据块的存储

白名单Decommission In Progress

介绍

白名单就是只允许指定的DataNode连接集群,可以有效防止其他节点混入,保证集群的数据安全

配置

  1. 首先创建一个文件存储黑名单的文件(名称可以自定义),然后在里面写上黑名单的地址,直接把地址写上就可以
  2. 然后在hdfs-site.xml配置文件中增加dfs.hosts属性

	dfs.hosts
	<白名单文件路径>

  1. 刷新NameNode和ResourceManager
hdfs dfsadmin -refreshNodes
yarn rmadmin -refreshNodes

节点的扩展和退役

节点扩展

扩展新的DataNode节点很容易,只需要配置好分布式的那些配置(尤其是指定好NameNode),DataNode就会自动向NameNode发送注册请求,然后加入集群

注意

不需要在配置中配置slave就可以添加新的节点,但是为了下次可以直接从NameNode启动新的DataNode节点,还是建议在slave里添加该新DataNode节点

节点退役

如果想要是某个DataNode节点退出集群,有多种方式可以选择

  • 直接关闭连接

直接将要退出的DataNode节点关机或者断开与NameNode的连接,一关时间之后NameNode会认为该节点宕机,然后就会自动剔除该节点

但是这种方法需要额外的等NameNode检测(10分钟),而且不能重新启动该DataNode节点,一旦重新启动或者恢复连接,就会重新加入集群

  • 设置黑名单

配置一个黑名单,把该节点的地址写进去,然后刷新NameNode和ResourceManager,该DataNode就会自动将自身的文件块备份到其他节点,然后退出集群,但是就像刚刚黑名单介绍的,此时DataNode仍会与NameNode保持连接,只不过是不再分配资源了

在生产环境中,推荐使用黑名单的方式退役节点

  • 设置白名单

配置一个白名单,然后白名单中不加上该节点的名字,刷新NameNode和ResourceManager,该节点就会被强制退出集群,并且关闭DataNode进程


HDFS小文件处理

小文件在HDFS中可以说是噩梦般的存在,因为每个小文件在HDFS中也占用一个块和一个元数据,而且多个小文件的元数据也会占用大量的NameNode内存,寻址也很慢,所以HDFS中我们不建议存储小文件

如果没有办法避免小文件,Hadoop也提供了相应的处理措施,就是把多个小文件打包成一个har包,这个包在HDFS中只占用一个文件的元数据,而且寻址和读取也比较方便

  1. 首先启动yarn进程
start-yarn.sh
  1. 将某个目录里的的所有文件打包成一个har包
/bin/hadoop archive -archiveName <自定义包名>.har –p  <打包之前的路径> <打包后的har包路径>

然后将har包上传到HDFS就可以了,例如:

bin/hadoop archive -archiveName input.har –p  ./input   ./output
  1. 查看har包内容
hadoop fs -lsr 
  1. 解包
hadoop fs -cp har:// <解压后的路径>

taNode仍会与NameNode保持连接,只不过是不再分配资源了

在生产环境中,推荐使用黑名单的方式退役节点

  • 设置白名单

配置一个白名单,然后白名单中不加上该节点的名字,刷新NameNode和ResourceManager,该节点就会被强制退出集群,并且关闭DataNode进程


HDFS小文件处理

小文件在HDFS中可以说是噩梦般的存在,因为每个小文件在HDFS中也占用一个块和一个元数据,而且多个小文件的元数据也会占用大量的NameNode内存,寻址也很慢,所以HDFS中我们不建议存储小文件

如果没有办法避免小文件,Hadoop也提供了相应的处理措施,就是把多个小文件打包成一个har包,这个包在HDFS中只占用一个文件的元数据,而且寻址和读取也比较方便

  1. 首先启动yarn进程
start-yarn.sh
  1. 将某个目录里的的所有文件打包成一个har包
/bin/hadoop archive -archiveName <自定义包名>.har –p  <打包之前的路径> <打包后的har包路径>

然后将har包上传到HDFS就可以了,例如:

bin/hadoop archive -archiveName input.har –p  ./input   /
  1. 查看har包内容
hadoop fs -lsr har:///
  1. 解包
hadoop fs -cp har:/// <解压后的路径>

你可能感兴趣的:(大数据,#,Hadoop,大数据,hadoop,hdfs)