【问题标题】:HDFS - load mass amount of filesHDFS - 加载大量文件
【发布时间】:2015-08-13 07:15:06
【问题描述】:

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

问题是当我尝试使用以下命令将这些本地文件上传到 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 中是否有更好的方法?

非常感谢每条有用的评论!

【问题讨论】:

  • @rpc1 你显然不知道 hdfs 是如何工作的
  • @Thomas Jungblut 可能是)我只研究它....

标签: java hadoop hdfs cloudera heap-size


【解决方案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 Archives(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 风格的流式分析,这是一个不错的选择。如果延迟是一个问题,那么还有很多其他选择

【讨论】:

    【解决方案2】:

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

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

    您可以像他的回答中提到的 rpc1 那样执行此操作,方法是在您的 hadoop -put 命令前加上 HADOOP_HEAPSIZE=&lt;mem in Mb here&gt; 前缀。

    【讨论】:

    • 我会尝试再次增加我的堆大小。我想也许会有更好的选择来做到这一点。稍后我将使用这些小输入文件制作一些 HAR 文件来测试 MapReduce-Jobs。
    • Mapreduce 和 HDFS 根本不喜欢小文件。 Cloudera blogs 上的一篇较早的文章可能会带来一些清晰度。 注意:也许 Sqoop 或 Flume 可能是更适合您需求的工具。 当涉及 Hadoop 上的大数据时,您总是更擅长处理大文件。
    • 我之前看过这篇文章,对这个问题有了一些了解。但是,当您在本地系统上有大量小文件并且想要创建例如 HAR 文件时,问题仍然存在,您首先需要在 HDFS 中使用这些文件?!我去看看 Flume
    【解决方案3】:

    尝试增加 HEAPSIZE

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

    here

    【讨论】:

      【解决方案4】:

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

      export HADOOP_HEAPSIZE=1000
      export HADOOP_NAMENODE_INIT_HEAPSIZE="1000"
      

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

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-11-27
          • 1970-01-01
          • 1970-01-01
          • 2021-10-19
          • 2019-10-29
          • 1970-01-01
          • 2020-11-19
          • 2021-07-07
          相关资源
          最近更新 更多