在我们之前的几篇博文《Iceberg在Adobe的应用》《基于写入Iceberg的缓存的数据摄取》和《Iceberg的读取优化》中,我们了解了 Apache Iceberg 的诸多优势,看到了它是如何与 Adobe 体验平台(Adobe Experience Platform)的整体架构相适应的。在这篇博文中,我们将分享 Adobe 将超过 1PB 的数据集迁移到 Adobe 体验平台数据湖(Datalake)上的 Iceberg 的故事,具体介绍我们面临的挑战以及从中吸取的经验教训。
Adobe 体验平台是一个开放系统,用于驱动实时个性化体验。客户使用它来集中和标准化整个企业的数据,进而获得他们感兴趣的数据的 360 度视图。这一视图之后可以与多项智能服务搭配使用,以驱动跨多个设备的体验、举办有针对性的活动、将配置文件和其他实体细分为多个类别,并充分利用高级分析数据。我们数据湖架构的核心是底层存储。数据湖依赖一个 Hadoop 分布式文件系统(HDFS)兼容的后端来存储数据,如今它是 Azure 提供的基于云的存储方案(Azure 的 Gen2 Data Lake Service「ADLS」)。
图 1:Adobe 体验平台架构
问题
Adobe 体验平台目录服务(Catalog Service)提供了一种列出、搜索和配置数据集的方法,这里的数据集相当于关系数据库中的表。它可用来提供名称、描述、模式(schema)和申请权限等信息,以及 Adobe 体验平台上记录的所有元数据。随着时间的推移,摄取的数据越来越多,从目录查询元数据也越来越困难。引入 Iceberg 后,我们看到元数据的捕获和记录方式发生了转变。将客户迁移到 Iceberg 时,Adobe 体验平台的单租户存储架构给我们带来了一些有趣的挑战。每个租户都符合下列三种场景之一:
租户是完全在 Apache Iceberg 上的新客户。
租户是现有客户,正在积极构建混合 Iceberg 和旧数据集的新集成。
租户是只有旧数据集的现有客户。
图 2:使用 Apache Iceberg 的 Adobe 体验平台
下面是我们迁移的所有客户端各自所有的数据集大小的分布。
图 3:迁移到 Apache Iceberg 的生产数据集的大小和数量
迁移动机
Iceberg 迁移是将数据从一种表格式移动到 Iceberg,或将 Iceberg 作为一种表格式引入数据的过程。虽然这看起来很简单,但它涉及到存储内和表格式的更改。除了 Apache Iceberg 带来的开箱即用的一些好处之外,还有几个因素促使我们将客户迁移到 Iceberg:
解耦对目录(Catalog)的依赖:有了 Iceberg 后,元数据现在与数据文件位于同一位置,我们可以减少对元数据服务的依赖。Iceberg 的元数据现在是我们的真实来源,我们用它来查询和过滤数据。
更快的数据访问:Iceberg 的元数据可用于分区修剪和过滤,这提升了数据访问查询的性能,降低了执行成本。客户可以看到他们的数据访问查询得到改进,返回结果的速度更快,同时保持准确性。
清理:从以往经验来看,客户会构建概念验证来审查新特性或工作流程。这些数据集通常是与特性版本隔离的。作为迁移过程的一部分,我们清理了不再需要的陈旧数据集和工件。
未来迁移:我们意识到这是为未来的迁移奠定基础的好机会。从这一迁移过程中,我们学到了如何更好地安排这种类型的迁移方案。例如,我们可以构建一个数据集的离线版本,然后将旧版本切换到它上面,而对生产者和/或消费者几乎没有影响。
客户分类
当客户端与数据湖交互时,它们会根据其唯一的 GUID 和/或名称向数据集读写数据。数据集的元数据存储在目录中,用于在 Adobe 体验平台上配置分区行为、模式信息、策略实施和其他行为。我们需要制定一个计划,不仅要满足每个客户的停机时间和可用性限制,还要考虑他们维护目录中的元数据或 ADLS 上的数据的需求。每个客户对数据丢弃和/或元数据丢弃的舒适程度都不一样。根据我们的评估,我们将客户分为四类:
图 4:迁移类型
帐户重置(Account Reset):这些客户并不在乎从存储中删除他们的数据并重置相应的元数据。在为 Iceberg 流启用新数据集时,他们不会遇到任何停机时间。
元数据重置(Metadata Reset):这些客户并不关心元数据,只要他们的所有现有数据在新数据集下都可以访问就行。他们也应该不会遭遇停机时间,因为数据可以回填到新的支持 Iceberg 的数据集。
数据重置(Data Reset):这些客户不关心数据,但很在乎元数据的维护。由于我们需要从数据湖中删除现有数据并重置 Iceberg 摄取的数据集,因此他们会遇到一小段计划停机时间(一般是几分钟)。
迁移(Migrate):这些客户希望将所有现有的元数据和数据迁移到 Iceberg。这些客户对停机时间非常敏感,因为他们有活跃的数据流摄取和流出数据。当我们逐步将数据集转换为 Iceberg 时,预计他们会遭遇停机时间。
根据总账户活跃度和数量(大小),我们对所有存储桶做了优先级排序,并为每个客户设计了一个迁移计划。在本博文中,我们将重点介绍将客户(数据和元数据)迁移到 Iceberg 的过程。
迁移最佳实践
不管是什么类型的数据我们都会迁移过去,迁移策略的目标是提高性能和竞争力。如果迁移出了问题,可能会导致数据不够准确(出现冗余或损坏)。即便源数据完全可用并遵守数据策略,也可能发生这种情况。此外,源数据中本来就存在的问题在移植到 Iceberg 时都会被放大。
完整的数据迁移策略应该是能够解决问题,而不是产生更多问题的。不完整的计划就麻烦了,除了超出期限和预算之外,还可能会导致迁移项目完全失败。在规划和制定工作战略时,我们设定了管理整体迁移框架的基本标准。战略数据迁移计划应考虑以下关键因素:
最小化客户停机时间:客户读取和写入数据访问的请求受到的影响应该最小化。基本上,迁移过程应该对客户端透明,并且它们的请求应按预期执行。这里还要维护数据入口和出口服务级别协议(SLA)。
元数据谱系:数据集是通过在一段时间内摄取成批数据来准备的。我们必须保留元数据的批处理历史(及其谱系,lineage),这样可能依赖某些已知约定的客户端在数据集迁移到 Iceberg 之后也可以继续依赖它们。任何重置方案中都不会维护这种谱系,因为它需要删除数据或元数据或两者。
审计跟踪:如果全面迁移计划遇到了未知状态,它会共享所有见解,甚至跟踪整个迁移过程。它维护详细的审计跟踪和其他所有诊断信息,从而帮助排除意外情况或故障。我们还必须审核迁移后的数据,以确保迁移工作流的准确性。
灾难恢复和回滚:在规划和设计阶段,以及在整个实施和维护过程中,迁移计划都会经过测试以确保它最终会达到预期结果。迁移计划必须为边缘情况和已知的突发事件做好准备。如果我们遇到任何问题,迁移策略应该允许回滚和恢复;我们绝不会让某个数据集迁移不完整。
经济高效:无论我们选择哪种策略,迁移数据都会产生相关成本。Iceberg 节约的总预期成本应该大于“迁移数据并以该格式维护数据”的成本。迁移计划必须是稳健的和轻量级的过程,以让这种过渡尽可能无缝。相比之下,缩水版本可能无法处理人们在生产环境中可能遇到的所有复杂性。
迁移策略
构建迁移策略的方法不止一种,大多数策略属于以下两类之一:“大爆炸(big bang)”或“涓流(trickle)”。在大爆炸迁移中,完整的数据集传输过程在有限的时间窗口内完成,并且在迁移期间数据可能不可用。相反,涓流迁移会分阶段完成迁移过程。在实施过程中,新旧系统会并行运行,从而消除了停机或运维中断时间。迁移到 Iceberg 时,我们选择的迁移模型必须更富创造力,因为:
纯粹的大爆炸方法是不可接受的,因为客户执行读取或写入数据操作时可能会遇到中断,并且停机时间窗口不够大,无法一次迁移所有数据集。
涓流方法是不可行的,因为在技术上(在有限的时间范围内)支持对某部分数据写入 Iceberg 的数据集进行全表扫描是不可行的。作为一个平台,我们没有看到维护这种混合模型有什么好处。
根据我们自身的业务需求和要求,我们探索了两种将客户数据集迁移到 Iceberg 的策略。这两种策略都迎合了我们的独特用例;它们是从大爆炸和涓流迁移两种方法中各挑一些内容组建起来的。
就地升级
就地(in-place)迁移策略是将数据集升级为 Iceberg 表格式,而无需数据重述或重处理。这意味着在迁移期间不会修改数据文件,并且所有 Iceberg 元数据(清单、清单列表和快照)都是在数据范围之外生成的。我们本质上是在一个孤立的环境中重新创建所有元数据,并将它们与数据文件放在同一位置。这个实用程序要求我们迁移的数据集必须在 Spark 的会话目录(Session Catalog)中注册。
图 5:使用就地升级策略的 Adobe 体验平台架构
优点
避免全表扫描读取数据,因为没有数据重述(restatement)。
Iceberg 元数据是在不读取实际数据文件的情况下获得的(假设源数据在 Parquet 中):查看 Parquet 数据文件页脚来获取模式中每一列的最小和最大统计信息。文件路径和分区元数据是从 Spark 的会话目录(在 Memory Catalog 中)获取的。
我们在目录中抽象了数据集的表格格式。这样我们就能在幕后构建 Iceberg 元数据,而客户端可以继续以源格式读取此数据集。为所有摄取的数据生成了元数据后,我们就启用 Iceberg 以供消费。
灾难回滚很简单。如果我们观察到元数据文件中出现损坏(重复或丢失),我们只需删除元数据目录。
由于我们不会修改存储在目录中的元数据,因此会保留批次谱系。
缺点
该实用程序假定数据集未被修改(更新插入或删除)。如果在我们开始生成元数据后,表中有了新的修改,那么我们需要重新开始这个过程。这一过程提供最少的读取停机时间,但写入停机时间较长。
回滚是灾难恢复的唯一可选项。其他恢复方法并不简单,因为元数据文件(具有 GUID)是动态构建的,重新启动整个过程需要从头开始。
由于缺少检查点,或者当写入停机时间对某些客户是不可接受时,该过程容易频繁重启。
此过程需要将表元数据存储在 Spark 的会话目录中。我们需要修改这个开源实用程序,因为 Adobe 体验平台在 Spark 基础架构之外维护了一个目录。
在这个过程中,Iceberg 表以大爆炸的方式用单个快照创建。由于快照只有一个,数据读取过程一开始会很慢,但随着摄取的数据越来越多(创建更多快照)会逐渐改善。
由于这一工作流的基本性质,审计是不维护的。如果迁移失败,流程将出错。我们也无法跟踪分区、文件或批次(数据行)的元数据生成过程。
如果需要重述数据,此工作流就不能用了,因为源数据文件未调整。
影子迁移
在影子(shadow)迁移策略中我们遵循一个水合模型。我们将创建一个新的数据集,按批生成源数据集的影子。一旦影子赶上,我们将切换一个开关,将影子数据集与源数据集切换过来。下面是我们的迁移架构,展示了整个工作流程的关键部分。
图 5:使用影子迁移策略的 Adobe 体验平台架构
迁移服务
迁移服务(MS)是一个无状态、可扩展且与租户无关的迁移引擎。MS 用来迁移数据湖上的数据,这些数据是通过 Adobe 的数字营销解决方案启用的接触点收集的。MS 维护着一个迁移 worker 池,每个 worker 负责将符合体验数据模型(XDM)的一个数据集迁移到 Iceberg。每个 worker 都维护着一支专门的助手程序部队,它们的任务是分散迁移工作流。例如,我们同时迁移多个批次的数据集,同时,MS 可以处理属于单个客户端的许多同类数据集的迁移。数据湖上的客户端数据集可以由单个或一组 MS 实例迁移。下面我们回顾一下用于描述迁移工作流程的几个术语:
下面是各步骤的细节:
我们提供了一个我们打算迁移到 Iceberg 的数据集列表。如果未提供列表,则 MS 会探索数据湖以查找要迁移的数据集。
我们加载每个源的元数据并运行完整的审计和预检。这些检查帮助我们判断这个数据集是否可以迁移到 Iceberg,或者已经在 Iceberg 中,或者我们正在恢复其迁移。
一旦我们确定了必要的检查点,我们就会为一个现有的影子创建或恢复迁移;迁移隐藏在客户的数据湖权限之外。
每个迁移 worker 的任务是创建一个数据摄取管道,该管道将从源获取数据并写入影子。
每个 worker 执行一个迁移工作流,它是一组预定义的动作,并使用其助手程序来扇出每个动作的执行过程。
每个 worker 助手都运行在批次级别。它为每个摄取的源批次创建一个影子批次;维护其所有元数据(包括标签和外部 ID);保留谱系。
在迁移过程中,我们利用了在常规摄取工作流中所做的优化:写入-审计-发布(WAP)和写入缓冲。
当影子被水合时,摄取工作流将为每个摄取的影子批次生成必要的 Iceberg 元数据。
在影子赶上之后,我们运行审计检查以查看数据奇偶校验。这需要行计数匹配和模式检查。
审计通过后,我们将源置于维护模式,禁止写入数据集。这是我们使用来自影子的元数据作为初始蓝图为源重新生成 Iceberg 元数据的关键一步。
我们用新的文件和清单路径重写了影子的 Iceberg 元数据;用源的路径代替影子的路径。其他值都是逐字复制的。
生成源 Iceberg 元数据后,我们将数据文件重命名为源路径。重命名是一种轻量级元数据操作(即使对于大型数据集来说也是如此)。
我们还更新了存储在目录中的元数据。影子批次和数据集文件更新为指向源。旧的源批次和数据集文件被停用,以删除对陈旧的非 Iceberg 数据的引用。
我们解除维护标志,更新表格式元数据并恢复对数据集的写入。
最后,我们清理陈旧的工件,例如目录中的影子数据集,并将旧的源 parquet 数据重命名为一个临时暂存器,以便稍后做垃圾收集。
图 7:影子(Shadow)和源(Source)流程
优点
对于 GB 和 TB 级的数据,客户读取和写入的停机时间都很短,从几秒到几分钟不等。
我们记录了许多指标以跟踪进度,这有助于审计和剖析工作流程以找出瓶颈。我们可以通过影子批次、外部 ID 或标签跟踪每个源批次的迁移。
批次谱系在影子上重新创建,然后移植到源。尽管批次标识符已更改,但仍保留了外部 ID 的谱系。
删除影子可以实现灾难回滚和恢复,这会从目录中删除其相关元数据并从数据湖中删除数据。
迁移一个源时可以测试不同的配置。我们可以为每个要测试的配置创建一个新的影子并评估其影响。一旦我们完成了一个配置,我们就可以使用特定的影子恢复源的迁移。
MS 是无状态服务,所有迁移检查点都存储在目录中。这意味着可以重新启动 MS,然后它将从最后一个已知检查点恢复操作。
因为有预验证检查和数据重述,现有源中的错误不会被带到 Iceberg,确保了无缝迁移。
最后,数据损坏和丢失的可能性很小,因为:
源数据被重述并写入影子数据集路径。
影子数据针对行计数和模式匹配进行了验证。
影子数据在源中被重命名为不同的目录(因此它不会与旧的 parquet 数据冲突)。
这一工作流与表格式无关,并为未来的迁移奠定了基础。如果我们决定将客户端迁移到其他表格式,我们可以重用此工作流的部分内容。
缺点
由于客户端会继续将数据摄取到现有系统中,在源和影子之间保持数据同步是一项挑战。
灾难恢复很容易,但当工作流更新目录中元数据的时点就不一样了。如果我们需要回滚超过该时点的更改,则需要做外科手术修复。
迁移时,我们会在目录和 ADLS 中维护一份额外的源副本,这应该考虑在内。
为迁移建造管道是有成本的,需要在预算和项目进度表中加以考虑。
我们需要认真测试所有可能出现的场景,以了解迁移服务可能会如何反应。
选择策略
经过权衡,我们决定选择影子迁移而不是就地迁移策略。原因如下:
得到的教训
使用影子迁移策略将数据集迁移到 Iceberg 肯定有其挑战。以下是我们遇到的一些陷阱。
流量隔离和可扩展性
迁移服务的设计重用了现有的摄取架构来执行其业务逻辑。我们重新利用了现有的摄取框架——计算能力和管道;来实现这个工作流。这意味着属于迁移过程的数据摄取和客户触发的摄取流量会存在资源争用;实时数据或定期回填都可能出现争用。我们需要很多组件来隔离与迁移过程相关的流量并扩展工作流的各个部分。
重新排列各个批次的优先级:面对有限的资源和维护摄取 SLA 的需求,我们重新排列了迁移批次的优先级,以低于客户数据(实时或回填)的优先级进行摄取,即以较低的吞吐量摄取各个批次。
隔离计算工作区:在具有可配置 worker 的专用集群上执行用于摄取迁移批次的作业。我们根据更大的共享资源池的可用性来控制集群的扩展方式。
配置写入缓冲:Iceberg 是一种幕后表优化,其迁移不受常规摄取 SLA 的控制。这允许我们调整写入缓冲,使其针对压缩进行优化——缓冲更长的时间并将更大的文件写入磁盘。
重写 Iceberg 元数据
Shadow 迁移工作流的关键设计元素之一是我们能够根据影子的元数据成功为源表重新生成 Iceberg 元数据。根据数据的性质和我们的写入缓冲配置,某些表生成了大量快照,并且此逻辑的执行是在集群上的单个节点(驱动)上完成的。这让我们面临两个挑战——
元数据正确性:Iceberg 的元数据用于驱动查询执行,元数据中的不准确值可能导致查询返回错误结果。在大规模重写 Iceberg 元数据时,我们必须确保源或影子没有损坏或重复。
分布式写入:为了扩展更大数据集的元数据重写过程,我们利用 Spark 将所有 Iceberg 元数据作为数据帧读取,并将路径转换作为一个用户定义函数(UDF)来应用。这有助于在整个集群中实现分布式写入。
相关文章:
参考资料:
原文链接:https://medium.com/adobetech/migrating-to-apache-iceberg-at-adobe-experience-platform-40fa80f8b8de
评论