小文件指的是那些size比HDFS 的block size(默认64M)小不少的文件。而HDFS的问题在于没法颇有效的处理大量小文件。node
任何一个文件,目录和block,在HDFS中都会被表示为一个object存储在namenode的内存中,每个object占用150 bytes的内存空间。因此,若是有10million个文件,每个文件对应一个block,那么就将要消耗namenode 3G的内存来保存这些block的信息。若是规模再大一些,那么将会超出现阶段计算机硬件所能知足的极限。app
不只如此,HDFS并非为了有效的处理大量小文件而存在的。它主要是为了流式的访问大文件而设计的。对小文件的读取一般会形成大量从datanode到datanode的seeks和hopping来retrieve文件,而这样是很是的低效的一种访问方式。jvm
大量小文件在mapreduce中的问题ide
Map tasks一般是每次处理一个block的input(默认使用FileInputFormat)。若是文件很是的小,而且拥有大量的这种小文件,那么每个map task都仅仅处理了很是小的input数据,而且会产生大量的map tasks,每个map task都会消耗必定量的bookkeeping的资源。比较一个1GB的文件,默认block size为64M,和1Gb的文件,没一个文件100KB,那么后者没一个小文件使用一个map task,那么job的时间将会十倍甚至百倍慢于前者。oop
hadoop中有一些特性能够用来减轻这种问题:能够在一个JVM中容许task reuse,以支持在一个JVM中运行多个map task,以此来减小一些JVM的启动消耗(经过设置mapred.job.reuse.jvm.num.tasks属性,默认为1,-1为无限制)。另外一种方法为使用MultiFileInputSplit,它可使得一个map中可以处理多个split。post
为何会产生大量的小文件?this
至少有两种状况下会产生大量的小文件设计
1. 这些小文件都是一个大的逻辑文件的pieces。因为HDFS仅仅在不久前才刚刚支持对文件的append,所以之前用来向unbounde files(例如log文件)添加内容的方式都是经过将这些数据用许多chunks的方式写入HDFS中。orm
2. 文件自己就是很小。例如许许多多的小图片文件。每个图片都是一个独立的文件。而且没有一种颇有效的方法来将这些文件合并为一个大的文件。图片
这两种状况须要有不一样的解决方 式。对于第一种状况,文件是由许许多多的records组成的,那么能够经过件邪行的调用HDFS的sync()方法和append方法结合使用来解 决。或者,能够经过些一个程序来专门合并这些小文件(see Nathan Marz’s post about a tool called the Consolidator which does exactly this).
对于第二种状况,就须要某种形式的容器来经过某种方式来group这些file。hadoop提供了一些选择:
* HAR files
Hadoop Archives (HAR files)是在0.18.0版本中引入的,它的出现就是为了缓解大量小文件消耗namenode内存的问题。HAR文件是经过在HDFS上构建一个层次化的文件系统来工做。一个HAR文件是经过hadoop的archive命令来建立,而这个命令实 际上也是运行了一个MapReduce任务来将小文件打包成HAR。对于client端来讲,使用HAR文件没有任何影响。全部的原始文件都 visible && accessible(using har://URL)。但在HDFS端它内部的文件数减小了。
通 过HAR来读取一个文件并不会比直接从HDFS中读取文件高效,并且实际上可能还会稍微低效一点,由于对每个HAR文件的访问都须要完成两层index 文件的读取和文件自己数据的读取(见上图)。而且尽管HAR文件能够被用来做为MapReduce job的input,可是并无特殊的方法来使maps将HAR文件中打包的文件看成一个HDFS文件处理。 能够考虑经过建立一种input format,利用HAR文件的优点来提升MapReduce的效率,可是目前尚未人做这种input format。 须要注意的是:MultiFileInputSplit,即便在HADOOP-4565的改进(choose files in a split that are node local),但始终仍是须要seek per small file。
* Sequence Files
通 常对于“the small files problem”的回应会是:使用SequenceFile。这种方法是说,使用filename做为key,而且file contents做为value。实践中这种方式很是管用。回到10000个100KB的文件,能够写一个程序来将这些小文件写入到一个单独的 SequenceFile中去,而后就能够在一个streaming fashion(directly or using mapreduce)中来使用这个sequenceFile。不只如此,SequenceFiles也是splittable的,因此mapreduce 能够break them into chunks,而且分别的被独立的处理。和HAR不一样的是,这种方式还支持压缩。block的压缩在许多状况下都是最好的选择,由于它将多个 records压缩到一块儿,而不是一个record一个压缩。
将已有的许多小文件转换成一个SequenceFiles可能会比较慢。但 是,彻底有可能经过并行的方式来建立一个一系列的SequenceFiles。(Stuart Sierra has written a very useful post about converting a tar file into a SequenceFile — tools like this are very useful).更进一步,若是有可能最好设计本身的数据pipeline来将数据直接写入一个SequenceFile。