Hadoop小文件解决方案-基于NameNode内存和MapReduce性能解决方案
[TOC]
在第一篇文章中,我讨论了什么构成了一个小文件,以及为什么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语句中实现。例如,如果合并文本文件:
在Hive或Java MapReduce中实现这一点也同样容易。这些MapReduce作业在执行时显然需要集群资源,并且通常在非工作时间进行调度。但是,应该足够频繁地运行它们,这样小文件的性能影响就不会变得太大。通常在这些作业中内置额外的逻辑,以便只合并文件夹中对性能有显著影响的文件。在一个仅包含三个文件的文件夹中合并文件的性能优势不如在一个包含500个小文件的文件夹中合并文件。
检查文件夹以确定应合并哪些文件夹可以通过多种方式完成。例如,Pentaho数据集成作业可用于迭代HDFS中的一组文件夹,找到满足最小合并要求的文件夹。还有一个专门为此任务设计的预编写应用程序名为File Crush,这是一个由Edward Capriolo编写的开源项目。File Crush不受专业支持,因此不保证它将继续与未来版本的Hadoop一起使用。
批处理文件合并不会保留原始文件名。如果拥有原始文件名对于处理或了解数据来源非常重要,则批处理文件合并将不起作用。但是,大多数HDFS设计在文件夹级别而不是在每个文件中嵌入命名语义。采用这种做法会将文件名依赖性作为一个问题删除。
序列文件
当需要维护原始文件名时,一种非常常见的方法是使用Sequence文件。在此解决方案中,文件名作为密钥存储在序列文件中,文件内容作为值存储。下表给出了如何将小文件存储在序列文件中的示例:
如果您有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性能。
相关推荐
通过实现MapReduce计算结果保存到MySql数据库过程,掌握多种方式保存计算结果的技术,加深了对MapReduce的理解;创建maven项目,项目名称hdfs,这里不再说明。红色部分为增加内容: