背景
在Netflix,我们当前的数据仓库包含存储在AWS S3中的数百PB的数据,而且每天我们都会提取并创建其他PB的数据。在这种规模下,当数据进入我们的仓库时,通过优化存储布局(记录,对象,分区),我们可以获得大量的性能和成本优势。
这样的优化有几个好处,例如,节省了存储空间,缩短了查询时间,降低了下游处理的成本,并通过删除仅为了提高查询性能而编写的其他ETL来提高开发人员的工作效率。另一方面,这些优化本身必须足够便宜,以使其自身的处理成本超过其带来的收益。
我们构建了AutoOptimize,可以高效透明地优化数据和元数据存储布局,同时最大程度地提高成本和性能优势。
本文将列出AutoOptimize的一些用例,讨论有助于提高效率的设计原理,并介绍高级体系结构。然后深入研究AutoOptimize的合并用例,并分享一些结果和好处。
用例
我们发现了几个用例,其中诸如AutoOptimize的系统可以带来大量价值。一些优化是高性能数据仓库的先决条件。有时,数据工程师在提取的数据上编写下游ETL,以优化数据/元数据布局,从而使其他ETL过程更便宜,更快速。AutoOptimize的目标是集中进行此类优化,以消除重复的工作,同时比普通ETL更有效地进行工作。
合并
当数据通过实时数据提取系统进入数据仓库时,它的大小有所不同。这将导致整个分区中的小文件数量不断增加。将大量的较小文件合并为少数较大的文件可以使查询处理更快,并减少存储空间。
种类
分区中的预排序记录和文件可加快查询速度并节省大量存储空间,因为它可以实现更高级别的压缩。我们已经有一些具有排序阶段的现有表,以减少表存储并提高下游查询性能。
压实
现代数据仓库允许更新和删除预先存在的记录。Iceberg计划以增量文件的形式启用此功能。随着时间的流逝,增量文件的数量会增加,将它们压缩到其源文件可以使读取操作更加优化。
元数据优化
在Iceberg中,通过保留到元数据中文件位置的映射,将物理分区与逻辑分区分离。这使我们能够在元数据中添加其他索引,以使点查询更加优化。我们还可以重组元数据以使文件扫描更快。
设计原则
为了使AutoOptimize有效地优化数据布局,我们做出了以下选择:
及时与定期优化仅在需要时(基于更改的内容)优化给定的数据集,而不是盲目的定期运行。基本优化与完整优化允许用户在收益递减时进行优化,而不是二进制设置。例如,我们允许分区中包含一些小文件,而不是始终合并大小合适的文件。最小替换与完全覆盖仅替换所需的最小文件量,而不是完全清除覆盖。
这些原则通过提高效率和效率,同时降低数据处理中的端到端延迟来减少资源使用。
除了这些原则之外,还有其他一些设计注意事项可以支持和启用:
具有数据库和表优先级的多租户。自动(事件驱动)和手动(临时)优化。对最终用户的透明度。高级设计
> AutoOptimize High-Level Design
AutoOptimize分为2个子系统(服务和参与者),以使古玩交易决策与操作在较高层次上脱钩。职责的这种分离有助于我们独立设计,管理,使用和扩展子系统。
自动优化服务
服务是决策者。它决定响应传入事件时该做什么和何时去做。它负责侦听传入的事件和请求,并区分不同的表和操作的优先级,以充分利用可用资源。
服务中完成的工作可以进一步分为以下三个步骤:
观察:几乎实时聆听仓库中的变化。另外,响应最终用户手动创建的临时请求。
定向:为已更改的特定表收集调整参数。另外,根据积压情况调整表的资源分配或参与者的数量。
决定:使用此特定更改的正确参数确定最高价值的操作以及何时执行操作,具体取决于该操作在所有表和操作中的优先级如何降低。
在AutoOptimize中,该服务是使用Redis保持状态的Java(Spring Boot)应用程序的集群。
自动优化演员
AutoOptimize中的参与者负责实际工作(合并/排序/压缩等)。AutoOptimize Service将命令发送给参与者,以指定要执行的操作。Actor的工作是以分布式且容错的方式执行这些命令。
Autoptimize中的参与者是由Autoptimize服务管理的长期运行的Spark作业的池。
这不是故意的,但是我们发现模块化AutoOptimize决策流程的方式与OODA循环非常相似,因此决定使用相同的分类法。
其他组件
Iceberg我们使用Apache Iceberg作为表格格式。AutoOptimize依靠Iceberg的某些特定功能(例如快照和原子操作)以准确和可扩展的方式执行优化。
简而言之,AutoAnalyze可以为表找到最佳的调整/配置参数。它使用“假设”实验以及先前的经验和启发式方法来查找表的最合适属性。将来,我们将发布有关AutoAnalyze的后续博客文章。对于AutoOptimize,它可以查找表是否需要文件合并或建议目标文件大小和其他参数。
深入研究文件合并
文件合并是我们为AutoOptimize构建的第一个用例。以前,我们有一个名为Ursula的本地系统,负责将数据提取到基于Hive的仓库中。基于Ursula的管道还定期对提取的表分区执行文件合并。从那时起,我们将摄取内容移至Keystone,并将表布局移至Iceberg。
从Ursula迁移到基于Keystone / Iceberg的提取开始,需要替换Ursula文件合并。文件合并对于低延迟的流式传输管道来说是必需的,因为数据经常延迟到达并且不均匀。随着时间的推移,跨分区的小文件数量会减少,并且可能会产生一些严重的副作用,例如:
放慢查询速度。更多处理资源。增加存储空间。
AutoOptimize中文件合并的目标是有效减少副作用,同时不增加数据管道的额外延迟。
解决方案
本节将讨论一些有助于我们实现上述目标的解决方案。
及时优化
通过表更改事件触发自动优化文件合并。这使AutoOptimize能够以最小的延迟立即采取行动。但是,由事件驱动的问题在于,每次更改分区时都要扫描它们,这很昂贵。如果可以滚动方式从变更集中确定分区“有多嘈杂”,则可以消除来自快照的早期信号所造成的不必要的全分区扫描。
基本工作
在对整个分区进行扫描之后,“自动优化”将获得有关分区状态的更全面视图。我们可以在此阶段获得更准确的分区状态,并避免不必要的工作。
分区熵我们引入了一个称为分区熵(PE)的概念,用于在每个步骤中进行早期修剪,以减少实际工作量。这是有关分区状态的一组统计信息。我们在每次快照扫描后以滚动方式计算此值,并且在每次分区扫描后以更详尽的方式计算此值。
PE中处理文件大小的部分称为文件大小熵(FSE)。分区的FSE从分区中文件大小的均方误差(MSE)中得出。我们将术语FSE和MSE互换使用。
我们使用标准的均方误差公式:
在哪里,
N=分区中的文件数目标=目标文件大小实际=最小值(实际文件大小,目标)
扫描分区后,使用上面的公式可以很容易地计算出MSE,因为我们知道该分区中所有文件的大小。我们将每个分区的MSE和N存储在Redis中,以备后用。
在快照扫描阶段,我们得到一个提交定义,其中包含在提交中添加和删除的文件及其元数据(如大小,记录数等)的列表。我们使用以下公式,根据快照信息和先前存储的统计信息,以滚动方式来计算已更改分区的新MSE。
在哪里,
M=快照中添加的文件数Target=目标文件大小Actual=min(实际文件大小,目标)N=分区中先前存储的文件数MSE=先前存储的MSE
共同学习,写下你的评论
评论加载中...
作者其他优质文章