繁体   English   中英

如何减少我的代工厂数据集中的文件数量?

[英]How do I reduce the number of files in my foundry dataset?

我的数据集有 20000 个文件,每个文件都很小。 我将如何减少文件数量以及最佳数量是多少?

执行此操作的最直接方法是在转换结束时显式执行repartition() (如果分区计数从原始数字严格减少,则为coalesce()

这需要是您返回/写出结果之前的最后一次调用。

这看起来像:

# ...

@transform_df(
  # ... inputs
)
def my_compute_function(my_inputs):
  # ... my transform logic ...

  df = df.coalesce(500) 
  # df = df.repartition(500) # this also works but is slightly slower than coalesce
  return df

这是称为“bucketing”以供参考的先行步骤。

最佳存储桶数取决于您操作的数据规模。 成功构建后,通过观察磁盘上数据集的总大小来计算最佳存储区数量有些简单。

如果您的数据集大小为 128GB,您最终会希望得到 128MB 的文件,因此您的存储桶数为:

128 GB * (1000 MB / 1 GB) * (1 file / 128MB) -> 1000 files

注意:这不是一个精确的计算,因为由于 Snappy + Parquet 写出中使用的数据压缩,更改存储桶计数后的最终数据集大小会有所不同。 您会注意到文件大小与您预期的略有不同,因此在上述示例中您可能最终需要 1100 或 900 个文件

由于这是一个我不得不多次解决的问题,因此我决定编写一份更详细的指南,其中包含许多不同的技术、优缺点以及存在的理由。

为什么要减少文件数?

避免使用包含许多文件的数据集有几个很好的理由:

  • 读取性能可能会更差 当数据分散在许多小文件中时,轮廓(分析)等应用程序的性能会受到严重影响,因为执行程序必须承担从后备文件系统下载许多小文件的开销。
  • 如果后备文件系统是 HDFS ,许多小文件会增加 hadoop name-nodes 和 gossip 协议的堆压力。 HDFS 不能很好地处理许多小文件,因为它不会对文件系统中的文件列表进行流式处理/分页,而是构造包含所有文件的完整枚举的消息。 当您在 HDFS 中有数千万甚至数亿个文件系统对象时,这最终会遇到名称节点 RPC 消息大小限制(您可以在配置中增加)和可用堆内存(您可以在配置中增加) ...如果您有更多可用内存。)节点间通信变得越来越慢。
  • 转换变得更慢,因为(目前甚至对于增量转换)驱动程序线程必须从目录中检索当前视图中所有文件的完整列表,以及事务的元数据和出处(这只是切线相关,但这并不罕见许多文件与许多事务相关)
  • 转换可以 OOM 驱动程序,因为文件集和事务集在某些时间点保存在内存中。 这可以通过为驱动程序分配更大的内存配置文件来解决——但这会增加成本和/或减少可用于其他管道的资源。

为什么我们最终会在数据集中得到许多文件?

以包含许多文件的数据集结束通常是由以下三个原因之一引起的:

  • 摄取许多小文件的文件摄取
  • 产生许多小文件的(行为不端的)转换。 每次执行 spark 中的广泛操作时,都会发生改组。 例如,当执行groupBy时(这意味着 shuffle),spark 默认选择将数据重新分区为 200 个新分区,这对于增量转换来说太多了。 由于分区不当(下面讨论),转换也可能产生过多的输出文件。
  • 增量运行且频繁运行的管道。 每次管道运行并处理一段(通常很小)数据时,都会在每个数据集上创建一个新事务,每个数据集至少包含一个文件。

接下来,我将列出我所知道的减少数据集中文件数的所有方法,以及它们的缺点和优点,以及适用时的一些特征。

摄入时(magritte 变压器)

最好的选择之一是首先避免拥有许多文件。 当从类似文件系统的源中摄取许多文件时,像“连接转换器”这样的 magritte 转换器可能有助于将许多 CSV、JSON 或 XML 文件组合成一个单一的文件。 在适用时,串联然后应用 gzip 转换器是一种特别有效的策略,因为它通常可以将 XML 和类似文本格式的大小减少 94% 左右。

主要的限制是要应用这个,你需要

  • 每当摄取运行时都有多个文件可用(因此对于在频繁更新数据源上非常频繁地运行的摄取而言效果不佳)
  • 有一个数据源,为您提供可以连接的文件

也可以将许多文件压缩成更少的文件(使用 .tar.bz2、.tar.gz、.zip、.rar 等格式),但这随后需要知道这种文件格式的下游转换并手动解包(文档中提供了一个示例),因为代工厂无法透明地提供这些档案中的数据。 但是,没有预制的 magritte 处理器可以执行此操作,并且在我应用此技术的情况下,我会在摄取之前使用 bash 脚本来执行此任务,这无疑不太理想。

后台压缩

Foundry 中有一种新机制可以将您写入的数据集与读取的数据集分离。 本质上有一个后台作业在运行,它在您追加文件时将文件洗牌到优化的索引中,以便数据集的读取(大部分)可以转到这个优化的索引,而不是作者留下的(通常有点随意)数据布局。

这具有多种好处(例如自动生成针对最常见读取模式优化的数据布局),其中之一是它可以在后台“压缩”您的数据集。

从这样的数据集中读取时,您的读取基本上会命中索引以及输入数据集(其中包含尚未被后台进程合并到索引中的任何文件。)

最大的优点是这会在后台自动发生,无论您的数据摄取或转换有多混乱,您都可以简单地写出数据(在写入时没有性能命中并尽快将数据提供给消费者),同时仍然结束一个很好的分区数据集,文件很少(最终。)

这里的主要限制是,这仅适用于 Spark 本身可以理解的格式的数据集,例如 parquet、avro、json、csv ......在摄入前将它们打包成例如镶木地板。 这样,随着时间的推移,铸造厂仍然可以合并多个这些镶木地板文件。

此功能尚未对最终用户完全可用(但计划默认为所有内容启用。)如果您认为这是您的管道之一最理想的解决方案,您的 palantir POC 可以与团队一起开票启用此功能。

重新分配和合并

合并是 spark 中的一个操作,它可以减少分区的数量而没有广泛的依赖(spark 中唯一的这样的操作)。 合并很快,因为它最大限度地减少了混洗。 它的工作方式与以前的 spark 版本相比发生了变化(并且那里有很多相互矛盾的信息),但它通常比repartition快。 但是,它有一个很大的警告:它减少了整个 transform 的并行度

即使您在写入数据之前的最后coalesce ,spark 也会调整整个查询计划以使用更少的分区,从而减少使用的执行程序,这意味着您获得的并行度更低。

重新分区是类似的,但它插入了一个完整的洗牌阶段。 这带来了更高的性能成本,但这意味着从这个阶段出来的数据基本上可以保证分区良好(无论输入如何)。 虽然repartition本身有点昂贵,但它不会遇到在整个转换过程中减少并行度的问题。

这意味着总体而言,如果您最终写出的数据量与您之前所做的工作量相比不是那么大,那么使用repartition不是coalesce通常会获得更好的性能,因为处理数据的能力更多的执行者最终超过了shuffle的缺点。 根据我的经验,除非您的转换非常简单,否则repartition通常会在这里胜出。

一个值得讨论的特定用例是增量管道。 如果您的增量管道相对简单并且只执行例如映射和过滤,那么进行coalesce就可以了。 然而,许多增量管道也会读取非常大的数据集的快照视图。 例如,增量管道可能会接收一行新数据,并读取整个先前的输出数据集(可能数百万行),因此请查看该行是否已存在于输出数据集中。 如果它已经存在,则不发出任何行,如果它不存在,则附加该行。 将一小段增量数据与大型静态数据集等连接起来时,会发生类似的情况。

在这种情况下,转换是增量的,但它仍然受益于高并行性,因为它仍然处理大量数据。

我的粗略指导方针是:

  • 转换作为快照运行: repartition到一个合理的数字
  • 变换以增量方式运行并且不需要高并行度: coalesce(1)
  • 变换以增量方式运行,但仍然受益于并行性: repartition(1)

如果写入速度/流水线延迟非常重要,那么这些选项都不能接受。 在这种情况下,我会考虑背景压缩。

定期快照

作为前一点的扩展,为了保持增量管道的高性能,我喜欢在它们上安排定期快照,这允许我每隔一段时间重新分区数据集,执行基本上是“压缩”的操作。

我已经在这里描述了如何设置它的机制: 如何强制增量 Foundry Transforms 作业以非增量方式构建而不影响语义版本?

我通常会在周末安排快照。 在整个星期中,管道中的每个数据集(可能有数百个数据集)将累积数千或数万个事务和文件。 然后在周末,随着计划的快照在管道中滚动,每个数据集将被重新分区,例如,一百个文件。

空气质量指数

最近,AQE 在代工厂中可用。 AQE 本质上(出于本次讨论的目的)将coalesce操作注入到您已经进行了混洗操作的阶段,具体取决于前一个操作的结果。 这通常会改善分区(以及文件数量),但据报道在极少数情况下也会使情况变得更糟(但我自己没有观察到这一点)。

默认情况下启用 AQE,但如果您想尝试禁用它,您可以将其应用到转换中。

分桶和分区

分桶和分区与此讨论有些相关,因为它们主要是关于布置数据以优化读取数据的特定方法。 这些技术目前都不适用于增量管道。

一个常见的错误是写出由高基数列分区的数据集,例如时间戳。 在具有 1000 万个唯一时间戳的数据集中,这将导致(至少)输出数据集中有 1000 万个文件。

在这些情况下,应该修复转换,并通过应用保留删除旧事务(包含数百万个文件)。

其他黑客

其他压缩数据集的技巧也是可能的,例如创建“环回”转换以读取先前的输出并将其重新分区,或者手动打开数据集上的事务以重新写入它。

这些是非常hacky的,但在我看来是不可取的,应该避免。 如今,背景压缩主要以更优雅、更可靠和更少黑客的方式解决了这个问题。

暂无
暂无

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

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