Minerva -- Airbnb 的大规模数据指标系统 Part 2
这是 Airbnb Minerva 系列文章的第二篇,介绍了 Minerva 的设计原则、计算架构。原文链接:How Airbnb Standardized Metric Computation at Scale
简介
在本系列的第一篇文章[1]中介绍了 Airbnb 投入大量资源构建了 Minerva,一个单一真实指标度量数据平台。它将业务指标的创建、计算、服务和消费方式标准化。我们花了数年的时间迭代开发出了正确的度量指标基础架构和设计良好的用户体验。由于这项多年的投资,当 Airbnb 的业务去年因 COVID-19 被迫中断时,我们能够迅速将数据转化为可行的洞见和战略。
在这第二篇文章中,我们将深入研究 Minerva 的计算基础设施。具体来说,我们将展示如何通过声明性配置标准化数据集定义,解释数据版本控制如何使我们能够确保跨数据集一致性,并说明如何在零停机时间下高效地填充数据。在这篇文章的最后,读者将清楚地了解我们如何管理规模化的数据集,并为度量指标的计算打下坚实的基础。
Minerva 的设计原则
正如在第一部分[1]中所分享的,Minerva 是为了解决多年的成长的烦恼以及团队内部和跨团队的相关度量指标不一致问题而诞生的。基于我们管理测试指标库[2]的经验,我们为 Minerva 制定了六个设计原则。
我们构建 Minerva 的设计原则:
标准化(Standardized):在一个地方明确(没有歧义的)定义数据。任何人都能清楚查看定义,而不会产生困惑。
声明式(Declarative):用户定义“做什么”而不是“如何做”。最终用户不用关心计算、存储或服务度量的流程。
可伸缩(Scalable):Minerva 必须在计算和操作上都是可伸缩的。
一致性(Consistent):数据始终是一致的。如果定义或业务逻辑发生了更改,则会自动进行回填,保持数据是最新的。
高可用性(Highly available):以零停机时间和最小的数据消费中断的影响实现新的数据集替换旧的数据集。
良好的测试(Well tested):用户可以在将更改合并到生产环境之前,对其进行广泛的原型测试和验证。
在接下来的部分中,我们将展开介绍这里提到的每个原则,并重点介绍支撑这些原则的基础设施组件。最后,我们将介绍用户体验。
Minerva 是标准化的
你可能还记得,在第一部分中[1],Airbnb 的 core_data 模式是非常受欢迎的,但这实际上是一把双刃剑。一方面,core_data 标准化了表的使用,允许用户快速确定要基于哪些表构建自己的数据集。另一方面,它给集中式数据工程带来了沉重的负担,使其承担不可能完成的任务:把关并将无穷无尽的新数据集添加到新的和现有的核心表中。此外,基于 core_data 构建的下游管道造成了重复和分散的指标的扩散。我们从这一经验中了解到,表的标准化是不够的,而指标级别的标准化是实现可靠消费的关键。毕竟,用户并不会使用表,他们使用指标、维度和报告。
Minerva 的关注点在指标和维度,而不是表和列。在 Minerva 中定义指标时,用户需要提供重要的自描述元数据。配置文件中需要明确提供所有权、修改历史和指标描述等信息。在 Minerva 之前,这样的元数据通常只存在于各个机构的未被归档的知识库或分散在各种商业智能工具的图表定义中。而在 Minerva 中,所有定义都可以和代码一样进行版本控制。修改这些配置文件必须经过严格的审查过程,就像代码审查一样。
Minerva 配置系统的核心是事件源和维度源,它们分别对应于 Star Schema 设计中的事实表和维度表。
图 1:事件源和维度源是 Minerva 的基本构建模块
事件源定义构建指标的原子事件,维度源包含可以与度量指标一起使用的属性和切片。事件源和维度源一起用于定义、跟踪和记录 Airbnb 上的指标和维度。
Minerva 是声明式的
在 Minerva 之前,创建一个有洞察力的分析系统或者高可信和响应式的仪表板是很困难的。为了跟上产品变化、满足查询性能需求和减小度量偏差而产生的数据集的管理工作会很快把团队压得喘不过气来。Minerva 的关键价值主张之一是极大的简化这个冗长而耗时的工作流,以便用户能够迅速将数据转化为具有可行性的洞见。
图 2:改进数据科学工作流
通过 Minerva,用户可以简单地定义一个维度集,从而将 Minerva 的指标和维度连接起来产生一个对分析工作友好的数据集。和随机创建的数据集不同,维度集有几个理想的属性:
用户只需要定义“做什么”,不需要关心“如何做”。所有的实现细节和复杂性都不需要用户关心。
以这种方式创建的数据集可以保证遵循我们最好的数据工程实践,从数据质量检查、连接、回填,一切都是高效的并且具有很好的成本效益。
数据被有效存储,并经过优化以减少下游仪表板的查询时间,提升响应能力。
因为数据集在 Minerva 中的定义是透明的,所以我们鼓励度量指标的复用,并减少重复的数据集。
图 3:可编程反规则化生成用户可以轻松配置的维度集
通过关注“做什么”而不是“如何做”,Minerva 提高了用户的生产力,并最大限度地将时间花在他们的主要目标上:研究趋势,揭示见解,进行深入的实验。这种价值主张一直是 Minerva 保持稳定并被持续采用的动力。
Minerva 是可伸缩的
Minerva 从一开始就考虑到了可伸缩性。现在,Minerva 为数百位用户和 80 多个团队提供 5000 多个数据集,考虑开发、维护的成本和开销是最重要的。
Minerva 的核心设计原则是建立在 DRY(Do not Repeat Yourself,不要重复)原则基础上的, 这意味着我们将试图尽可能多地重用数据,从而减少计算的浪费,并确保一致性。计算流程可以分为几个不同的阶段:
数据获取阶段(Ingestion Stage):分区传感器(Partition sensors)等待从上游数据源获取数据,并将数据注入到 Minerva。
数据检查阶段(Data Check Stage):执行数据质量检查,将上游的缺陷数据过滤掉。
连接阶段(Join Stage):根据连接键(join keys)以可编程的方式连接数据从而生成维度集。
后处理和服务阶段(Post-processing and Serving Stage):连接的输出被进一步聚合,派生的数据被虚拟化以用于下游用例。
图 4:Minerva 计算流概要设计
在数据获取阶段,Minerva 等待获得上游表,并将数据提取到静态 Minerva 表中。这个输入的数据成为真实的数据源,需要修改 Minerva 配置才能更改。
数据检查阶段确保在执行进一步处理和创建维度集之前正确格式化源数据。以下是 Minerva 运行的一些典型检查:
源不应该是空的
时间戳不应该是 NULL,并且应该满足 ISO 标准
主键应该是唯一的
维度值应该与预期一致
对于连接阶段,不同维度集中引用的相同数据是从具有相同转换逻辑的相同上游表中获取、计算和连接的。如果有的中间数据集可以增强系统的整体效率,那么中间数据集也会被添加进去。如上面的图表所示,这种集中计算是我们确保在大规模数据集上进行一致和有效计算的关键。
最后,在后处理和服务阶段,有可能会进一步优化最终用户查询性能,并虚拟化派生数据。我们将在本系列的第三篇文章中深入探讨这一阶段的更多细节。
除了在计算上具有可扩展性外,我们还需要平台提供更好的操作效率。有一些关键特性帮助我们达到这一目的。具体来说,我们实现了智能自动修复、批量自动回填和智能报警。
自动修复允许 Minerva 优雅的从各种瞬态问题中自动恢复,例如:
管道或平台代码中的 Bug
不稳定的基础设施,如集群或调度程序中断
上游数据由于错过了 SLA 而导致的超时
为了实现自动修复,Minerva 必须具备数据感知能力。每次作业启动时,Minerva 都会检查现有数据,以确定是否有丢失的数据。如果识别出丢失的数据,它会自动将其包含在当前的执行流程中。这意味着一次运行就可以动态地决定计算窗口和回填数据。当任务由于瞬时问题而失败时,不需要用户手动重置任务。
图 5:从失败的运行中识别和计算丢失的数据,作为未来运行的一部分
这种自动修复逻辑还会导致自动回填。如果 Minerva 识别出相关数据版本不存在数据,它将自动开始从其上游数据集生成数据。如果回填窗口很长(例如,几年),它可能会生成一个长时间运行的查询。虽然我们的底层计算引擎应该具有足够的可伸缩性,以处理大量的查询,但让一个查询长时间运行仍然有风险:长时间运行的查询对瞬时基础设施故障很敏感,如果失败,恢复的成本会很高,并造成资源使用高峰。在另一个极端,使用一个很小的回填窗口,比如一天,又会太慢,不能很好的适应需要更长时间的工作。为了提高可伸缩性、减少运行时资源占用以及提高可恢复性,我们实现了批处理回填。
图 6:在 2021 年 05 月 01 日的任务中,单个作业被分成几个并行的月批次
对于批处理回填,Minerva 根据特定数据集的可伸缩性,将作业划分为几个日期范围。例如,Minerva 可以将两年的回填数据拆分为 24 个月批次,并行运行。失败的批将在下次运行时自动重试。
这种自动化的数据集管理还有利于团队之间的责任划分。基础设施问题由平台团队负责,而数据问题由各自的产品或数据科学团队负责。不同的数据集需要不同的告警级别。Minerva 根据错误类型智能地向适当的团队发出警报,并通知下游消费者数据将会有延迟。这有效分担了整个公司的运营负载,同时将责任分配给最适合解决根本原因的团队。通过这种告警系统设计,我们避免了在多个团队之间进行问题识别、分类的工作。
自动修复、自动批量回填和智能报警是使得 Minerva 成为一个低维护成本、高操作效率、高弹性系统的重要特点。
Minerva 是一致性的
Minerva 的度量指标库经常被许多用户更改,并且发展非常迅速。如果我们不仔细协调这些变化,很可能指标和维度会不一致。如何确保 Minerva 生成的数据集始终是一致的和最新的?
我们的解决方案就是所谓的数据版本,它是配置文件中指定的所有重要字段的哈希值。 当我们更改任何影响生成数据的字段时,数据版本就会自动更新。每个数据集都有一个唯一的数据版本,所以当版本更新时,会自动创建一个新的数据集并进行回填。下面的示例说明了这种机制是如何工作的。
图 7:对单个维度的更新可以触发使用该维度的所有数据集的回填
在图 7 所示的场景中,更新了维度源 1 中的某个维度。如果这个维度被两个维度集(即 A123 和 B123)使用,那么与这两个维度集相关的数据版本也将相应的被更新。由于现在更新了数据版本,对这两个维度集的回填工作将自动启动。在 Minerva 中,新变化导致新数据版本的循环,从而触发新的回填,这使我们能够在数据集之间保持数据一致。这种机制确保上游的更改以一种可控的方式传播到所有下游的数据集,并且没有 Minerva 数据集会偏离单一的真实数据源。
Minerva 是高可用的
既然我们已经解释了 Minerva 如何使用数据版本控制来维护数据一致性,热心的用户可能已经注意到了一个两难的境地:回填率与用户更改率相互竞争。在实践中,回填往往赶不上用户的更改,特别是当更新影响许多数据集时。由于 Minerva 只提供了一致且最新的表层数据,因此快速变化的数据集可能会永远处于回填模式,并导致大量数据不可用。
为了解决这个问题,我们创建了一个称为 Staging 环境的并行计算环境。Staging 环境是由待定的用户配置修改构建的生产环境的副本。通过在共享环境中自动完成回填作业,Minerva 将多个未发布的更改应用到一组回填作业中。这至少有两个好处:1)用户不再需要跨团队协调更改和回填,2)数据消费者不再经历数据停顿时间。
Staging 环境的数据流程如下:
用户在本地环境中创建和测试新的更改。
用户将更改合并到 Staging 环境。
Staging 环境加载 Staging 配置,并补充任何必要的生产环境配置,开始填充任何修改过的数据集。
充填完成后,Staging 配置合并到生产环境中。
生产环境立即获取新的定义,并利用它们为消费者提供数据。
图 8:配置更改首先被加载到 Staging,然后在准备发布时合并到 Production
即使在用户经常更新定义的情况下,Staging 环境也能允许我们对关键业务指标保证一致性和可用性。这对于公司内许多大规模数据迁移项目的成功至关重要,并且在我们专注于数据质量时,有助于改进我们的数据仓库[3]。
Minerva 可以很好的支持数据测试
定义指标和维度是一个快速迭代的过程。用户通常会发现原始数据不合理,或者需要进行更深入的挖掘,以理解源数据是如何生成的。作为构建在自动生成的数据集之上的指标和维度的真实来源,Minerva 必须帮助用户验证数据的正确性,清楚地解释正在发生的事情,并加快迭代周期。
图 9:使用 Minerva 原型工具的用户开发流程
为了做到这一点,我们创建了一个引导原型工具,它从生产环境中读取数据,但却写入一个独立的沙箱。与 Staging 环境类似,该工具利用 Minerva 管道执行逻辑在用户的本地修改之上快速生成示例数据。这允许用户利用新的和现有的数据进行质量检查,同时还提供示例数据,可以根据用户的假设和(或)现有数据验证输出。
该工具可以清楚的显示 Minerva 管道用来生成输出所遵循的一步一步的计算,从而提高了 Minerva 计算逻辑的可见性,帮助用户独立调试问题,并为 Minerva 平台开发团队提供了一个优秀的测试环境。
最后,该工具基于用户配置的日期范围和抽样来限制被测试数据的大小。这大大加快了执行时间,将迭代时间从几天减少到几分钟,同时允许数据集保留验证所需的许多统计属性。
整合:COVID-19 案例研究
接下来我们将介绍一个应用案例,看看分析师 Alice 是如何通过 Minerva 将数据转化为公司的洞见,从而说明这一切是如何整合在一起的。正如我们在第一篇文章[1]中所描述的,COVID-19 完全改变了人们通过 Airbnb 旅行的方式。从历史上看,Airbnb 对城市和非城市目的地的需求相当均衡。在新冠疫情开始时,Alice 假设旅行者会避开大城市,选择与其他旅行者保持社交距离的目的地。
为了证实这一假设,Alice 决定分析 nights_booked(房间预定)指标,通过 dim_listing_urban_category(城市房间)指标进行裁剪。她知道在 Minerva 中已经定义了 nights_booked 指标,因为这是公司最重要的指标(top-line metric)。然而,她所关心的指标维度在 Minerva 中并不容易获得。Alice 和她的团队利用了 NASA(美国宇航局)创建的全球农村-城市地图项目(Global Rural-Urban Mapping Project)[4]和 GPW v4 世界人口密度数据(GPW v4 World Population Density Data)[5],用这个新的元数据为所有指标进行标记。然后,她开始使用这个新的数据集来制作新的 Minerva 维度原型。
图 10:Alice 在维度源中配置新维度
Alice 还在整个公司用于跟踪 COVID-19 对业务运营影响的几个维度集中加入了新的维度定义。
图 11:Alice 将新维度添加到 Central Insights 团队拥有的 COVID SLA 维度集
为了在 Minerva 中验证这个新维度,Alice 使用了上面描述的原型工具来计算这个新维度的数据样本。几分钟之后,她就能够确认她的配置是有效的,数据被准确地合并了。
图 12:Alice 能够在几分钟内与她的队友共享样本数据
在验证数据之后,Alice 向 Core Host 团队提交了一个 pull request 以供代码审查,该团队拥有所有指标元数据的定义。这个 pull request 包括执行日志,计算成本估计,以及示例数据的链接,以方便对方查看。在收到批准后,Alice 将更改合并到共享的 Staging 环境中,在几个小时内,修改后的数据集的整个历史将自动返回,并最终合并到生产环境中。
图 13:由于 Alice 提交的变更,公司里每个人都可以清楚地看到客人需求的变化
通过新创建的数据集,全公司的团队和领导者开始在仪表盘上强调和跟踪用户行为的这些变化。这一关键性能指标的变化也导致我们计划修改关键产品页面,以适应用户新的旅行模式。
图 14:跨事件源(y 轴)采用新的维度源(红色)
通过这个流程,Alice 能够定义一个新的维度,并将其附加到已经存在的指标上,获得域所有者的批准,并在数天内跨多个团队更新大量关键数据集。所有这些都是通过几十行 YAML 配置完成的。
总结
在这篇文章中,我们探讨了 Minerva 的计算基础设施及其设计原则。我们着重介绍了用户如何使用详细的元数据在 Minerva 中定义标准化数据。通过声明性的用户界面,用户只需要关注“做什么”而不是“如何做”。因为 Minerva 经过了良好的测试和一致性,所以我们能够与用户建立信任。最后,Minerva 的可伸缩设计使我们能够扩展我们的足迹,推动公司内部的采用和数据的标准化。
随着 Minerva 在公司内部变得无处不在,用户发现他们的数据工作变得更容易了。我们致力于让 Minerva 在未来变得更好。我们近期路线图中的一些项目包括利用 Apache Iceberg[6]作为下一代存储表格格式,扩展到实时数据,并支持更复杂的定义,等等。
在本系列的下一篇文章中,我们将转而讨论 Minerva 如何利用上述一致性和可用性保证将用户从底层数据集中解放出来。我们将介绍遇到的挑战,着重介绍我们在 Minerva API 上做的事情,并解释我们如何将 Minerva 与 Airbnb 数据栈的其余部分整合在一起,从而在我们的度量层提供统一的以度量为中心的消费体验。请继续关注我们的下一篇文章!
References:
[1] https://xie.infoq.cn/article/c9e946329f726b22d770cc765
[2] https://medium.com/airbnb-engineering/https-medium-com-jonathan-parks-scaling-erf-23fd17c91166
[3] https://medium.com/airbnb-engineering/data-quality-at-airbnb-e582465f3ef7
[4] https://sedac.ciesin.columbia.edu/data/collection/grump-v1
[5] https://sedac.ciesin.columbia.edu/data/collection/gpw-v4
评论