在第一篇文章中,我评论了什么构成了一个小文件,以及为什么Hadoop存在小文件问题。我将一个小文件界说为小于Hadoop块大小75%的任何文件,并解说说由于NameNode内存运用和MapReduce功用,Hadoop更喜爱较少的较大文件。在这篇文章中,当小文件真实不可避免时,我将评论这些应战的处理方案。

处理NameNode内存问题


正如之前的文章中所评论的,Hadoop中每个块的元数据有必要存储在NameNode的内存中。这导致实际限制Hadoop中能够存储的目标数量,而且还会影响发动时间和网络带宽。有两种处理方案,减少Hadoop集群中的目标数量,或以某种办法使NameNode更多地运用内存 – 但不会导致过多的发动时间。处理此内存问题的最常用办法涉及Hadoop存档(HAR)文件和联合NameNodes。

Hadoop存档文件


Hadoop归档文件通过将许多小文件打包到更大的HAR文件中来缓解NameNode内存问题,类似于Linux上的TAR文件。这导致NameNode保留单个HAR文件的常识,而不是数十个或数百个小文件。能够运用har://前缀而不是hdfs://来拜访HAR文件中的文件。HAR文件是从HDFS中存在的文件创立的。因而,HAR文件能够兼并吸取的数据以及通过正常的MapReduce处理创立数据。能够独立于用于创立小文件的技能来运用HAR文件。除了HDFS之外没有一同的依赖。

虽然HAR文件减少了许多小文件的NameNode内存占用,但拜访和处理HAR文件内容的功率或许会降低。HAR文件仍然随机存储在磁盘上,而且读取HAR内的文件需求两个索引拜访 – 一个用于NameNode以找到HAR文件本身,一个用于在HAR内查找小文件的位置。在HAR中读取文件实际上或许比读取本机存储在HDFS上的相同文件慢。MapReduce作业会影响此功用问题,由于它们仍将在HAR中的每个文件中发动一个map使命。

最后,你有一个HAR文件能够处理NameNode内存问题,但或许会恶化处理功用。假如您的小文件首要用于存档意图,而且不常常拜访,那么HAR文件是一个很好的处理方案。假如小文件是正常处理流程的一部分,您或许需求重新考虑您的规划。

Federated NameNodes


Federated NameNodes允许您在群会集拥有多个NameNode,每个NameNode都存储目标元数据的子集。这消除了将一切目标元数据存储在单个机器上的需求,从而为内存运用提供了更多的扩展。从表面上看,用这种技能处理小文件内存问题很有吸引力,可是略微想一想你会很快意识到这些局限性。

Federated NameNodes阻隔目标元数据 – 只要一个NameNode知道任何特定目标。这意味着要获取文件,您有必要知道要运用哪个NameNode。假如您的群集包括多个租户或孤立的应用程序,那么Federated NameNode很天然 – 您能够通过租户或应用程序阻隔目标元数据。可是,假如要在群会集的一切应用程序之间同享数据,则此办法并不理想。

由于Federated实际上不会更改群会集的目标或块的数量,因而它无法处理MapReduce功用问题。相反,Federated为您的Hadoop安装和办理增加了重要且通常不必要的复杂性。当用于处理小文件问题时,通常更多的是隐藏小文件问题的机制。

处理MapReduce功用问题


MapReduce功用问题是由随机磁盘IO和发动/办理太多map使命的组合引起的。处理方案好像很明显 – 拥有更少,更大的文件或发动更少的map使命; 可是,这说起来简单做起来难。一些最常见的处理方案包括:

  • 更改吸取进程/距离
  • 批处理文件兼并
  • 序列文件
  • HBase
  • S3DistCp(假如运用Amazon EMR)
  • 运用CombineFileInputFormat
  • Hive配置
  • 运用Hadoop的附加功用
更改吸取进程/距离

摆脱小文件的最简略办法就是不首要生成它们。假如源系统生成数千个复制到Hadoop的小文件,请查询更改源系统以生成一些大文件,或者在吸取到HDFS时或许衔接文件。假如您每小时仅吸取10 MB数据,请确定是否每天只能吸取一次。您将创立1x240MB文件而不是24x10MB文件。可是,您或许无法控制创立文件的源系统或业务需求要求您以距离频率接纳数据,以便小文件不可避免。假如小文件确实是不可避免的,那么应该考虑其他处理方案。

批处理文件兼并

当小文件不可避免时,文件兼并是最常见的处理方案。运用此选项,您能够定期运转一个简略的兼并MapReduce作业来读取文件夹中的一切小文件,并将它们重写为更少的大文件。假如文件夹中有1000个文件,而且MapReduce作业仅指定5个文件,则1000个输入文件将兼并为5个输出文件。接下来是一些简略的HDFS文件/文件夹操作,您将内存占用减少了200:1,而且或许提高了对同一数据的未来MapReduce处理的功用。

这能够在Pig,load和store语句中完成。例如,假如兼并文本文件:

[外链图片转存失利,源站或许有防盗链机制,主张将图片保存下来直接上传(img-Y4oa4JDF-1669338670929)(/projects/hadoop_small_files/157c5a89665a98dc.png)]

在Hive或Java MapReduce中完成这一点也同样简单。这些MapReduce作业在执行时显然需求集群资源,而且通常在非工作时间进行调度。可是,应该足够频繁地运转它们,这样小文件的功用影响就不会变得太大。通常在这些作业中内置额外的逻辑,以便只兼并文件夹中对功用有显著影响的文件。在一个仅包括三个文件的文件夹中兼并文件的功用优势不如在一个包括500个小文件的文件夹中兼并文件。

检查文件夹以确定应兼并哪些文件夹能够通过多种办法完成。例如,Pentaho数据集成作业可用于迭代HDFS中的一组文件夹,找到满意最小兼并要求的文件夹。还有一个专门为此使命规划的预编写应用程序名为File Crush,这是一个由Edward Capriolo编写的开源项目。File Crush不受专业支持,因而不保证它将继续与未来版别的Hadoop一同运用。

批处理文件兼并不会保留原始文件名。假如拥有原始文件名对于处理或了解数据来源非常重要,则批处理文件兼并将不起作用。可是,大多数HDFS规划在文件夹等级而不是在每个文件中嵌入命名语义。采用这种做法会将文件名依赖性作为一个问题删去。

序列文件

当需求维护原始文件名时,一种非常常见的办法是运用Sequence文件。在此处理方案中,文件名作为密钥存储在序列文件中,文件内容作为值存储。下表给出了如何将小文件存储在序列文件中的示例:

[外链图片转存失利,源站或许有防盗链机制,主张将图片保存下来直接上传(img-uHy8YbwF-1669338670929)(/projects/hadoop_small_files/157c5aafe7cf0368.png)]

假如您有10,000个小文件,则您的序列文件将包括10,000个密钥,每个文件一个。序列文件支持块紧缩,而且是可拆分的,这意味着MapReduce作业每个128MB块只能发动一个map使命,而不是每个小文件一个map使命。当您需求维护输入文件名,而且同时吸取数百或数千个小文件时,这非常有用。

可是,假如您一次只提取少数小文件,则序列文件也不能正常工作,由于Hadoop文件是不可变的,无法追加。三个10MB文件将发生30MB的序列文件,根据咱们的界说,这仍然是一个小文件。另一个应战是检索序列文件中的文件名列表需求处理整个文件。

此外,Hive在此结构中与序列文件不兼容。Hive将值中的一切数据视为单行。运用Hive查询此数据并不简单,由于文件的整个内容将是Hive中的单行。最后,您创立的Hive表将无法拜访序列文件密钥,文件名,而且只能拜访值,即文件的内容。能够编写自界说Hive serde来处理这些应战,但这是一个逾越Hadoop本机功用的高级功用。

结论


咱们评论了运用Hadoop Archive(HAR)文件来最小化NameNode内存运用的权衡。咱们评论并驳回了运用Federated NameNodes作为小文件问题的灵丹妙药。而且,咱们为小文件整合引入了一些常用的处理方案 – 这些处理方案能够提高NameNode内存运用率和MapReduce功用。