HDFS小文件问题

个人理解

  1. hadoop

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

    • 因为每个文件最少占用一个block,每个block的元数据都会在namenode节点占用内存。存储大量小文件的话,它会占用NameNode大量的内存来存储文件目录和块信息。这样是不可取的,因为NameNode的内存总是有限的,NameNode的内存溢出会导致文件无法写入。
    • 小文件存储的寻址时间会超过读取时间,它违反了HDFS的设计目标。
  2. mapreduce

    FileInputFormat generates splits in such a way that each split is all or part of a single file.

    一个小文件在map输入时就是一个分片,分片的数量等于启动的MapTask的数量。Map Task数量过多的话,会产生大量的小文件, 过多的Mapper创建和初始化都会消耗大量的硬件资源 。(Map Task数量过少,就会导致并发度过小,Job执行时间过长,无法充分利用分布式硬件资源。)

  3. hive

    • 虽然map阶段都设置了小文件合并,set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;,太多小文件导致合并时间较长,查询缓慢;

    • Hive对于小文件有一种补救措施,参数hive.merge.smallfiles.avgsize控制hive对output小文件的合并,当hiveoutput的文件的平均大小小于hive.merge.smallfiles.avgsize默认为16MB左右,hive启动一个附加的mapreducejob合并小文件,合并后文件大小不超过hive.merge.size.per.task默认为256MB。

      尽管Hive可以启动小文件合并的过程,但会消耗掉额外的计算资源,控制单个reduce task的输出大小>64MB才是最好的解决办法。

小文件对HDFS的危害

在大数据环境,很多组件都是基于HDFS,例如HDFS直接放文件环境、以及HBase、Hive等上层数据库环境。如果对HDFS环境未进行优化,小文件可能会造成HDFS系统的崩溃。今天我们来看一下。

一、究竟会出什么问题

因为HDFS为了加速数据的存储速度,将文件的存放位置数据(元数据)存在了NameNode的内存,而NameNode又是单机部署,如果小文件过多,将直接导致NameNode的内存溢出,而文件无法写入。

为此在HDFS中放小文件必须进行优化,不能将小文件(类似1MB的若干小文件)直接放到HDFS中。

二、数据在DataNode中如何存储?

HDFS默认的数据存储块是64MB,现在新版本的hadoop环境(2.7.3版本后),默认的数据存储块是128MB。

一个文件如果小于128MB,则按照真实的文件大小独占一个数据存储块,存放到DataNode节点中。同时 DataNode一般默认存三份副本,以保障数据安全。同时该文件所存放的位置也写入到NameNode的内存中,如果有Secondary NameNode高可用节点,也可同时复制一份过去。NameNode的内存数据将会存放到硬盘中,如果HDFS发生重启,将产生较长时间的元数据从硬盘读到内存的过程。

如果一个文件大于128MB,则HDFS自动将其拆分为128MB大小,存放到HDFS中,并在NameNode内存中留下其数据存放的路径。不同的数据块将存放到可能不同的DataNode中。

三、如何解决小文件需要存放到HDFS的需求?

1.合并小文件,数据未落地到HDFS之前合并或者数据已经落到HDFS,用spark service服务或其它程序每天调度去合并。Apache官方也提供了官方工具,Hadoop Archive或者HAR,是一个高效地将小文件放入HDFS块中的文件存档工具,它能够将多个小文件打包成一个HAR文件,这样在减少namenode内存使用的同时,仍然允许对文件进行透明的访问。(但对于MapReduce 来说起不到任何作用,因为har文件就相当一个目录,仍然不能讲小文件合并到一个split中去,一个小文件一个split)

2.多Master设计,让元数据分散存放到不同的NameNode中。

也许还有同学会提到增大NameNode的内存、甚至将元数据直接从硬盘中读取,但这些方法都是治标不治本,不适用。

四、小文件的其它危害

小文件除了可能会撑爆NameNode。另一个是hive或者spark计算的时候会影响它的速度,因为spark计算时会将数据从硬盘读到内存,零碎的文件将产生较多的寻道过程。

五、题外话:HDFS为什么将Block块设置为128M

1、如果低于128M,甚至过小。一方面会造成NameNode内存占用率高的问题,另一方面会造成数据的寻址时间较多。

2、如果于高于128M,甚至更大。会造成无法利用多DataNode的优势,数据只能从从一个DN中读取,无法实现多DN同时读取的速率优势。

参考链接:

https://cloud.tencent.com/developer/article/1512285