繁体   English   中英

HDFS - 加载大量文件

[英]HDFS - load mass amount of files

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

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

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

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

线程“main”中的异常 java.lang.OutOfMemoryError: Java heap space

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

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

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

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

小文件和 HDFS 的问题

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

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

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

小文件和 MapReduce 的问题

Map 任务通常一次处理一个输入块(使用默认的 FileInputFormat)。 如果文件很小并且有很多,那么每个映射任务处理的输入很少,并且映射任务更多,每个任务都会带来额外的簿记开销。 比较分成 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)对它们进行处理。 还有一些奖金。 SequenceFiles 是可拆分的,因此 MapReduce 可以将它们分成块并独立操作每个块。 与 HAR 不同,它们也支持压缩。 在大多数情况下,块压缩是最佳选择,因为它压缩多个记录的块(而不是每个记录)

HBase

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

尝试增加 HEAPSIZE

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

这里

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

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

您可以像他的回答中提到的rpc1一样这样做,方法是在您的hadoop -put命令前添加HADOOP_HEAPSIZE=<mem in Mb here>前缀。

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

export HADOOP_HEAPSIZE=1000
export HADOOP_NAMENODE_INIT_HEAPSIZE="1000"

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

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

<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 字符。 在此之后,您只需解析结构并拥有文档标识符和内容。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM