2

出于测试目的,我正在尝试将大量小文件加载到 HDFS 中。实际上,我们谈论 100 万(1'000'000)个大小从 1KB 到 100KB 的文件。我在一个文件夹中的 Linux 系统上使用 R-Script 生成了这些文件。每个文件都有一个信息结构,其中包含带有产品信息的标题和带有数字信息的不同数量的列。

问题是当我尝试使用以下命令将这些本地文件上传到 HDFS 时:

hdfs dfs -copyFromLocal /home/user/Documents/smallData /

然后我得到以下 Java-Heap-Size 错误之一:

线程“主”java.lang.OutOfMemoryError 中的异常:Java 堆空间

线程“主”java.lang.OutOfMemoryError 中的异常:超出 GC 开销限制

我使用 Java-Heap-Size 约 5 GB 的 Cloudera CDH5 发行版。除了增加这个 Java-Heap-Size 之外,还有其他方法吗?将如此大量的数据加载到 HDFS 中也许是一种更好的方法?

我非常感谢每一个有用的评论!

4

5 回答 5

1

如果您将增加内存并将文件存储在 HDFS 中。在此之后,您将在处理时遇到许多问题。

小文件和 HDFS 的问题

小文件是明显小于 HDFS 块大小(默认 64MB)的文件。如果您要存储小文件,那么您可能有很多文件(否则您不会转向 Hadoop),问题是 HDFS 无法处理大量文件。

HDFS 中的每个文件、目录和块都表示为 namenode 内存中的一个对象,根据经验,每个对象占用 150 个字节。因此,1000 万个文件,每个文件使用一个块,将使用大约 3 GB 的内存。扩大到这个水平是当前硬件的一个问题。当然十亿个文件是不可行的。

此外,HDFS 并不适合高效访问小文件:它主要是为大文件的流式访问而设计的。读取小文件通常会导致大量的搜索和从数据节点到数据节点的大量跳跃来检索每个小文件,所有这些都是低效的数据访问模式。

小文件和 MapReduce 的问题

Map 任务通常一次处理一个输入块(使用默认的 FileInputFormat)。如果文件非常小并且数量很多,那么每个 map 任务处理的输入非常少,并且有很多 map 任务,每个任务都会带来额外的簿记开销。比较一个分成 16 个 64MB 块的 1GB 文件和 10,000 个左右 100KB 的文件。10,000 个文件每个都使用一张地图,作业时间可能比使用单个输入文件的同等文件慢几十或几百倍。

有几个特性可以帮助减轻簿记开销:任务 JVM 重用以在一个 JVM 中运行多个映射任务,从而避免一些 JVM 启动开销(请参阅 mapred.job.reuse.jvm.num.tasks 属性)和 MultiFileInputSplit每张地图可以运行多个拆分。

解决方案

Hadoop 档案(HAR 文件)

创建.HAR文件 Hadoop 存档(HAR 文件)在 0.18.0 中被引入 HDFS,以缓解大量文件对 namenode 内存造成压力的问题。HAR 文件通过在 HDFS 之上构建分层文件系统来工作。使用 hadoop archive 命令创建一个 HAR 文件,该命令运行 MapReduce 作业以将正在归档的文件打包成少量 HDFS 文件

hadoop archive -archiveName name -p <parent> <src>* <dest> 
hadoop archive -archiveName foo.har -p /user/hadoop dir1 dir2 /user/zoo

序列文件

对“小文件问题”问题的通常回答是:使用 SequenceFile。这里的想法是使用文件名作为键,文件内容作为值。这在实践中非常有效。回到 10,000 个 100KB 的文件,您可以编写一个程序将它们放入单个 SequenceFile,然后您可以在 SequenceFile 上以流式方式(直接或使用 MapReduce)处理它们。还有一些奖金。SequenceFile 是可拆分的,因此 MapReduce 可以将它们分成块并独立地对每个块进行操作。与 HAR 不同,它们也支持压缩。在大多数情况下,块压缩是最好的选择,因为它压缩了多条记录的块(而不是每条记录)

HBase

如果您要生成大量小文件,则根据访问模式,不同类型的存储可能更合适。HBase 将数据存储在 MapFiles(索引 SequenceFiles)中,如果您需要偶尔随机查找进行 MapReduce 风格的流式分析,这是一个不错的选择。如果延迟是一个问题,那么还有很多其他选择

于 2015-08-14T05:49:54.720 回答
0

Try to increase HEAPSIZE

HADOOP_HEAPSIZE=2048 hdfs dfs -copyFromLocal /home/user/Documents/smallData 

look here

于 2015-08-13T07:45:23.757 回答
0

为了解决这个问题,我构建了一个具有某种格式的文件。文件的内容都是小文件。格式将是这样的:

<DOC>
  <DOCID>1</DOCID>
  <DOCNAME>Filename</DOCNAME>
  <DOCCONTENT>
    Content of file 1
  </DOCCONTENT>
</DOC>

这种结构可能或多或少是场,但思路是一样的。例如,我使用了这种结构:

<DOC>
  <DOCID>1</DOCID>
  Content of file 1
</DOC>

并处理超过 600 万个文件。

如果您希望为一个地图任务处理每个文件,您可以删除和标签之间的 \n 字符。在此之后,您只需解析结构并拥有文档标识符和内容。

于 2015-08-15T02:53:21.897 回答
0

Hadoop分布式文件系统不适合小文件多,大文件多。HDFS 在查找表中保存一个记录,该表指向 HDFS 中的每个文件/块,并且该查找表通常加载到内存中。所以你不应该只增加 java 堆大小,还应该增加 hadoop-env.sh 中名称节点的堆大小,这是默认值:

export HADOOP_HEAPSIZE=1000
export HADOOP_NAMENODE_INIT_HEAPSIZE="1000"

如果您要对这些文件进行处理,您应该期望在它们上运行的第一个 MapReduce 作业的性能较低(Hadoop 创建的映射任务数量作为文件/块的数量,这将使您的系统过载,除非您使用 combineinputformat )。建议您将文件合并为大文件(64MB/128MB)或使用其他数据源(不是 HDFS)。

于 2015-08-13T23:11:36.430 回答
0

首先:如果这不是对您的名称节点的压力测试,那么不建议这样做。但我假设你知道你在做什么。(预计这方面进展缓慢)

如果目标只是在 HDFS 上获取文件,请尝试以较小的批次执行此操作,或者在您的 hadoop客户端上设置更高的堆大小。

您可以像他的回答中提到的rpc1HADOOP_HEAPSIZE=<mem in Mb here>一样通过在您的hadoop -put命令前加上前缀来执行此操作。

于 2015-08-13T07:53:17.443 回答