Hadoop 大量小文件问题(译)

原文:http://blog.cloudera.com/blog/2009/02/the-small-files-problem/

HDFS 中的小文件问题

小文件是指文件大小明显小于 HDFS 块(block)大小(默认 64MB)的文件。大量这样的小文件 Hadoop 的扩展性和性能带来严重问题。

首先,在 HDFS 中任何一个文件,目录或者数据块在 NameNode 节点的内存中均以一个对象表示(元数据),并且受到 NameNode 物理内存容量的限制。每个元数据对象约占 150byte,所以如果有1千万个小文件,每个文件占用一个 block,则 NameNode 大约需要 2G 空间。如果存储1亿个文件,则 NameNode 需要 20G 空间。

其次,处理小文件并非 Hadoop 的设计目标,HDFS 的设计目标是流式访问大数据集(TB 级别)。因而,在 HDFS 中存储大量小文件是很低效的。访问大量小文件经常会导致大量的寻址,以及不断的从一个 DatanNode 跳到另一个 DataNode 去检索小文件,严重影响性能。

最后,处理大量小文件速度远远小于处理同等大小的大文件的速度。每一个文件要占用一个 slot(不论大小),而 Task 启动将耗费大量时间甚至大部分时间都耗费在启动 Task 和释放 Task 上。

MapReduce 中的小文件问题

Map 任务一般一次处理一个块大小的输入(input)(默认使用 FileInputFormat)。如果文件非常小,并且拥有大量的这种小文件,那么每一个 Map task 都仅仅处理非常小的 input 数据,因此会产生大量的 map tasks,每一个 map task 都会额外增加开销。一个 1GB的文件,拆分成 16 个块大小文件,相对于拆分成 10000 个 100KB 的小文件,后者每一个小文件启动一个 map task,那么 job 的时间将会十倍甚至百倍慢于前者。

Hadoop 中有一些特性可以用来减轻开销:可以在一个 JVM 中允许 Task JVM 重用,以支持在一个 JVM 中运行多个 Map task,以此来减少 JVM 的启动开销(通过设置 mapred.job.reuse.jvm.num.tasks 属性,默认为1,-1表示无限制。如果有大量小文件,每个小文件都要启动一个 map task,则必启动 JVM,这提供的一个解决方案就是重用 task 的 JVM,以此减少 JVM 启动开销);另 一种方法是使用 MultiFileInputSplit,它可以使得一个 map 中能够处理多个块。

为什么会产生大量的小文件

至少有两种场景下会产生大量的小文件:

(1)这些小文件都是一个大逻辑文件的一部分。HDFS 在 2.x 版本才开始支持对文件的 append,所以在此之前保存无边界文件(持续产生的文件,例如日志每天都会生成)一种常用的方式就是将这些数据以块的形式写入 HDFS 中

(2)文件本身就是很小。设想一下,我们有一个很大的图片语料库,每一个图片都是一个独一的文件,并且没有一种很好的方法来将这些文件合并为一个大的文件。

解决方案

这两种情况需要有不同的解决方式。

第一种情况

对于第一种情况,文件是许多记录(Records)组成的,那么可以通过调用 HDFS 的 sync() 方法(和 append() 方法结合使用),每隔一定时间生成一个大文件。或者可以通过写一个程序来来合并这些小文件。

第二种情况

对于第二种情况,就需要某种形式的容器通过某种方式来对这些文件进行分组。Hadoop 提供了一些选择。

HAR File

Hadoop Archives (HAR files)是在 0.18.0 版本中引入到 HDFS 中的,它的出现就是为了缓解大量小文件消耗 NameNode 内存的问题。HAR 文件是通过在 HDFS 上构建一个分层文件系统来工作。HAR 文件通过 hadoop archive 命令来创建,这个命令实际上是运行了一个 MapReduce 作业来将小文件打包成少量的 HDFS 文件(将小文件进行合并几个大文件)。

对于 client 端来说,使用 HAR 文件没有任何的改变:所有的原始文件都可见以及可访问(只是使用 har://URL,而不是 hdfs://URL),但是在 HDFS 中中文件数却减少了。

读取 HAR 中的文件不如读取 HDFS 中的文件更有效,因为每个 HAR 文件访问需要读取两个索引文件以及还要读取数据文件本身(如下图)。尽管 HAR 文件可以用作 MapReduce 的输入,但是没有特殊的方式允许 MapReduce 直接操作 HAR 在 HDFS 块上的所有文件。 可以考虑通过创建一种 input format,充分利用 HAR 文件的局部性优势,但是目前还没有提供这种 input format。在目前看来,HARs可能最好仅用于存储文档

hadoop-har

SequenceFile

通常对于”小文件问题”的回应会是:使用序列文件(SequenceFile)。这种方法的思路是,使用文件名(filename)作为 key,并且文件内容(file contents)作为 value。在实践中这种方式非常有效。回到 10,000 个 100KB 小文件问题上,可以编写一个程序将它们放入一个单一的 SequenceFile,然后流式处理它们(直接处理或使用 MapReduce)操作 SequenceFile。这样同时会带来两个优势:

(1)SequenceFiles 是可拆分的,因此 MapReduce 可以将它们分成块并独立地对每个块进行操作;

(2)支持压缩, 在大多数情况下,块压缩是最好的选择,因为压缩几个记录为一个块,而不是一个记录压缩一个块。

hadoop-seq

将现有数据转换为 SequenceFile 可能很慢。 但是,完全可以并行创建 SequenceFile 的集合。Stuart Sierra 写了一篇关于将 tar 文件转换为 SequenceFile 的文章(https://stuartsierra.com/2008/04/24/a-million-little-files ),像这样的工具是非常有用的。

HBase

如果你生产很多小文件,那么根据访问模式,不同类型的存储可能更合适。HBase 以 Map Files(带索引的 SequenceFile)方式存储数据,如果需要随机访问来执行 MapReduce 式流式分析,这是一个不错的选择。

Tags:

Add a Comment

电子邮件地址不会被公开。 必填项已用*标注