简介
在本系列的第一篇文章中,我们介绍了 Minerva 在改善 Airbnb 数据分析工作方面所起的作用。在第二篇文章中,我们深入探讨了 Minerva 的核心计算基础设施,并介绍了我们如何保证数据集和团队数据的一致性。在第三篇也是最后一篇文章中,我们将重点讲述 Minerva 如何极大的简化和改善用户的数据消费体验。具体来说,我们将展示统一指标层(我们称之为 Minerva API)如何帮助我们构建为具有广泛背景和不同级别数据专业知识的用户量身定制的多功能数据消费体验。
以指标为中心的方法(A Metric-Centric Approach)
当用户采用数据来探索业务问题时,通常会考虑不同的指标和维度。例如,业务负责人可能想知道长期住宿(维度)占预订(指标)的百分比是多少。要回答这个问题,首先要找到正确的表单(where),通过必要的联合(joins)或过滤(filters)(how),最终聚合数据(how)以得到正确的答案。
虽然许多传统 BI 工具试图代表用户把这些工作抽象出来,但大多数数据服务逻辑仍然严重依赖用户来确定“where”和“how”。在 Airbnb,我们希望提供更好的用户体验——用户只要简单的请求获取指标和维度,就可以直接得到答案,而不必担心“where”或“how”的问题。我们将这一愿景称之为“以指标为中心的方法”,最终发现这是一个艰巨的工程挑战。
挑战一:“Where”
在大多数传统数据仓库中,数据以表的形式组织。这意味着要响应某个查询,BI 工具需要将相关的指标和维度与包含相关答案的物理表关联起来。然而,对于给定的指标和维度的组合,也许有许多数据集包含有相关答案。这些表通常具有不同程度的数据质量和正确性保证,因此选择正确的表来服务数据并非易事。
挑战二:“How”
除了“where”之外,负责“how”的数据服务逻辑也有许多细微差别。首先,有不同的指标类型:由单个物理事件(例如:预定量)组成的简单指标(simple metrics);基于维度过滤产生的一组简单指标组成的过滤指标(filtered metrics)(例如:中国的预订量);由一个或多个非派生指标组成的派生指标(derived metrics)(例如:图书搜索匹配率)。此外,虽然有许多指标是递增的(例如:预订量),但也有许多指标不是:计数差、百分比和基于时间的快照等,不能简单的通过汇总单个事件来计算。始终如一的在所有场景中正确的计算这些不同类型的指标是一个巨大的挑战。
挑战三:与下游应用程序集成
最后,只有在各种上下文环境、应用程序和工具中使用数据,才能做出基于数据的决策。指标越通用、越重要,就越有可能被广泛应用于各种场合。例如,总预订价值(Gross Booking Value,GBV)、预订夜数(nights booked)和收入(revenue)是 Airbnb 最常用的指标,被广泛应用于跟踪业务表现、作为随机控制实验的基准比较指标,并用于比较机器学习模型。在不同用例中基于这些指标提供服务,同时为用户提供上下文信息从而可以以正确的方式使用它们,是我们面临的另一个核心挑战。
解决方案
我们通过构建 Minerva API 来解决这些挑战,这是一个指标服务层(metric-serving layer),充当上游数据模型和下游应用程序之间的接口。有了 Minerva API,任何下游应用程序都能够以一致和正确的方式消费数据,而不用知道数据存储在哪里,也不用知道应该如何计算指标。本质上,Minerva API 通过连接“what”和“where”来充当“how”。
Minerva API
Minerva API 由 API web 服务、元数据获取应用以及客户端(与 Apache Superset[2]、Tableau[3]、Python[4]和 R[5]集成)组成。这些组件为下游应用程序提供本地 NoSQL 和 SQL 指标查询。
Minerva API 充当消费者和底层数据集之间的接口
元数据获取器:抽象“Where”
我们前面提到过,用户只需要向 Minerva 提供指标和规格参数,而不需要指定“where”。当发出数据请求时,Minerva 会花费大量精力来确定应该使用哪个数据集来响应该请求。
Minerva 在幕后选择最佳数据源之前需要综合考虑多个因素,其中最重要的因素之一是数据完整性。这意味着选择用于查询的任何数据源都应该包含给定用户查询请求所需的所有列,并且必须涵盖查询请求所需的时间范围。
为此,我们构建了一个名为元数据获取器(Metadata Fetcher)的服务,该服务每 15 分钟定期从数据源获取元数据,并将其缓存到 MySQL 数据库中。具体来说,我们定期从 S3 获取 Minerva 配置的最新副本(存储在 Thrift 二进制文件中),从而获取 Druid 中每个有效 Minerva 数据源的列表。对于每个数据源,我们查询 Druid 代理以读取它的名称以及相关的指标和维度列表。此外,我们还可以从代理获取最小日期、最大日期以及日期计数,以确定是否有任何丢失的数据。每次获取新信息时,我们都会更新 MySQL 数据库,以维护真实数据源。通过元数据获取器,我们能够在任何给定的时间使用最好的数据源来服务数据请求。
数据 API:抽象“How”
假设用户希望了解 2021 年 8 月的 4 周时间内,除私人房间外,各目的地地区的日均价格(average daily price,ADR)下降趋势。示例查询的完整规格定义如下所示:
{ metric: ‘price_per_night’, groupby_dimension: ‘destination_region’, global_filter: ‘dim_room_type!=”private-room”’, aggregation_granularity: ‘W-SAT’, start_date: ‘2021–08–01’, end_date: ‘2021–09–01’, truncate_incomplete_leading_data: ‘true’, truncate_incomplete_trailing_data: ‘true’, }
当 Minerva 接收到这样的请求时,它不仅需要确定从哪里获取数据,还需要知道如何过滤、组合以及聚合数据以获得最终的结果。它采用了一种策略,通过 Split-Apply-Combine 范式[6]来实现,该范式通常用于数据分析。
对'price_per_night'指标应用 Split-Apply-Combine 范式
步骤一:将请求拆分为原子指标请求
当 Minerva API 接收到如上所述的查询请求时,它所做的第一件事就是通过创建一组相关的子查询,将任何派生指标分解为我们称为 Minerva“原子”指标。如果一个用户查询只指定一个原子的 Minerva 指标,那么第一步基本上是一个空操作。
在上面的例子中,给定‘price_per_night’指标是一个比率指标(派生指标的一种特例),它包含一个分子(‘gross_booking_value_stays’)和一个分母(‘nights_booking’),Minerva API 将这个请求分解为两个子请求。
步骤二:采用并执行每个子查询
对于第 1 步中确定的原子指标,Minerva 利用 S3 中存储的指标配置来推断相关的指标表达式和元数据,从而生成子查询。我们继续讨论这个例子:Minerva 数据 API 查找“gross_booking_value_stays”的指标定义,发现它是一个 SUM 聚合,类似的,“nights_booking”指标也是如此。在这两个请求中,通过全局过滤器' dim_room_type != " private-room" '用于确保私人房间不在计算范围内。
对 ADR 指标应用 Split-Apply-Combine 范式
一旦为每个原子指标都生成了关联的子查询,Minerva API 最终将查询发送给 Druid 或 Presto。它将查询分割成几个跨越更小时间范围的“片”,然后在达到资源限制时将结果合并到单个数据帧中。在基于聚合粒度拼接数据帧之前,API 还会丢弃任何不完整的前置或后置数据。
步骤三:将原子指标结果合并到单个数据帧中
一旦 Minerva 获取到每个原子指标的数据帧,它会通过连接时间戳列上的数据帧将它们组合成一个单独的数据帧。作为最后一步,Minerva API 在以序列化 JSON 格式将最终结果返回给客户端之前将执行任何必要的聚合后计算、排序和限制操作。
总之,通过 Minerva 的数据源 API 和数据 API,我们可以抽象出确定从哪里获取数据以及如何返回数据的过程。这个 API 作为 Minerva 的单一抽象层,可以满足来自下游应用程序的任何请求。然而,我们的故事并没有就此结束:我们的许多工程挑战都涉及到如何将不同的应用程序与这个 API 集成,我们将在下一节探讨这些挑战。
数据消费经验
考虑到 Airbnb 内部数据消费者的多样性,我们开始构建针对不同角色和用例的工具。通过 Minerva API,我们构建了广泛的用户界面,这些用户界面提供了一致的数据消费体验。正如我们在第一篇文章中简要提到的,有四个主要的集成点,每个点支持一组不同的工具和用户:
- 数据分析(Data Analysis):与 Python 和 R 集成,主要用于高级数据分析
- 数据探索(Data Exploration):与 BI 工具(如 Superset、Metric Explorer[7]和 Tableau)的集成,为精通数据的分析师量身定制,以帮助商业洞察
- 报告(Reporting):与 XRF(eXecutive Reporting Framework,执行报告框架)[8]集成,为希望了解当前业务状态的管理层量身定制
- 实验(Experimentation):与 ERF(Experimentation Reporting Framework,实验报告框架)集成,专为在 Airbnb 进行 A/B 测试的数据科学家、工程师或产品经理量身定制
当我们构建这些特性时,我们总是在一致性、灵活性和可访问性之间进行权衡。例如,Metric Explorer 主要是为非数据专家的非技术用户构建的,这意味需要为它优化一致性和可访问性,而不是灵活性。Metric Explorer 有严格的执行保护,防止用户做错误的事情,并且几乎没有机会偏离确定的道路。
作为另一个极端,通常受数据科学家青睐的 R 和 Python 客户端要灵活得多。用户可以完全控制如何利用客户端 API 来执行定制分析或可视化。在接下来的几节中,我们将介绍这些消费体验是如何被创建的。
与 Metric Explorer 集成
Metric Explorer 由 Airbnb 创建,任何人(无论他们的数据专业水平如何)都可以利用数据做出明智的决策。由于其面向广泛的目标用户,Metric Explorer 优化了可访问性和数据一致性,而不是灵活性。
Metric Explorer 对于想要回答高级业务问题的非技术用户来说非常适合
所有 Metric Explorer 的指标、维度和相关元数据都来自 Minerva 的指标存储库,并被注入到 Elasticsearch[9]中。在用户对数据执行任何操作之前,这些元数据作为上下文方便的显示在右侧栏上。
当用户选择执行 Group By 和 Filter 之类的数据操作时,Metrics Explorer 按等级顺序显示维度,这样只有很少或没有业务上下文的用户可以轻松的挖掘信息,而不需要提前知道维度值(如上所示)。
当用户对数据进行切片时,Minerva API 会自动确定哪个组合是有效的,并且只会对有效的数据组合进行切割。在这种体验中,用户不需要知道任何有关所涉及指标来源的底层物理表的信息。
与 Apache Superset 集成
虽然 Metrics Explorer 提供了有关参数的高级信息,但更有探索精神的用户可以在 Superset 中进行更多操作。Apache Superset[10]是 Airbnb 自助 BI 解决方案的核心工具。考虑到 Superset 在公司内的广泛应用,我们知道需要提供类 SQL 的功能,从而与 Superset 进行集成,以便 Minerva 能够被广泛采用。
用于 Apache Superset 和 Tableau 等 BI 工具的客户端接口要复杂得多,很多应用可以选择直接通过 RESTful 接口调用 Minerva API。这些 BI 工具通常使用 SQL(通过客户端),而不是 HTTP 请求进行访问。这意味着 Minerva API 需要支持类 SQL 接口,该接口需要遵循 OLAP[11]查询结构。为了构建这样一个接口,我们利用 sqlparse[12]在 Minerva API 中添加了一个 SQL 解析器,用于将 SQL 语句解析为 AST,然后对其进行验证并将其转换为本地 HTTP 请求。
遵循 DRY 原则,我们复用 Apache Calcite Avatica[13]定义了客户机和服务器之间的通用数据库连接 API。Minerva API 充当 Avatica HTTP 服务器,客户端要么是基于 SQLAlchemy[14]定制的 Python Database API[15]数据库驱动程序,要么是 Avatica 提供的 JDBC 连接器(Tableau)。
传统 BI 工具在工具内部实现自定义业务逻辑,而 Minerva 通过类 SQL 的 AGG 指标表达式来整合这些逻辑。下表中,我们比较了在传统 BI 工具和 Superset 工具中运行的查询:
在左边的查询中,用户不需要指定指标应该从哪里计算,也不需要指定正确的聚合函数——这些细节都被 Minerva 抽象了。
最后,假设 Minerva 中有 12,000 个指标和 5,000 个维度,但并不是所有的指标-维度组合都是有效的。例如,活动列表可以通过主机所在的位置来切割,但不能通过客人的出发位置来切割(也就是说,每个预订的客人属性可能不同)。我们在图表控件中添加了事件监听器,以确保左侧视窗中只显示符合条件的指标和维度的组合。这种设计有助于减少认知负载,简化数据挖掘过程。
Superset 是以指标为中心的,用户可以从单个虚拟源查询所有指标和维度
与 XRF(eXecutive Reporting Framework)集成
如第一篇所述,XRF 是一个框架,用于生成由执行人员和领导团队使用的简洁、高保真的业务关键报告。这个框架是通过 Minerva 的配置来配置的,并且完全由 Minerva API 提供支持。
XRF 自动化了大量重复的手工工作,并允许我们标准化高保真的业务关键报告
要管理 XRF 报告,用户首先需要定义报告配置,并指定所需的业务指标、维度切片以及需要应用的全局筛选器。此外,用户还可以配置其他控制行为,比如某个指标是否需要执行聚合(如 MTD、QTD 或 YTD)操作,以及为基于时间的比较增长率(如 YoY、MoM 或 WoW)指定合适的单位。一旦指定了这些设置,Minerva API 就会执行必要的聚合操作以及生成最终的报告。
XRF 输出的数据可以通过自定义 GoogleSheetHook 渲染在 Google 表格中,也可以通过 Presto 连接到 Tableau 中。通过利用 Minerva 及其聚合逻辑中的指标定义,我们在用户选择的表示层中强制执行一致性保障。
与 ERF(Experimentation Reporting Framework)集成
与分析或报告用例不同,实验用例比较特殊,用于报告的指标只是一个起点。为了做出正确的因果推论,在将指标转换为可用于有效统计比较的汇总统计数据之前,必须将指标与实验分配的数据结合起来。
通常,Minerva 向 ERF 提供“原始事件”。根据随机单元和分析单元,我们使用不同的主题键将 Minerva 数据加入到分配日志中,以便每个事件都有相关的主题,以及与之相关的实验组。最后计算并汇总统计信息(如平均值、百分比变化和 p 值)并显示在 ERF 记分卡中。
显示实验统计摘要的 ERF 记分卡
实验 UI 还会直接显示相关的 Minerva 元数据,用户还可以查看 Minerva 事件的描述和所有权信息。一个带有 ETA 信息的谱系视图允许用户跟踪 ERF 指标进展[16],并帮助他们在出现延迟的情况下联系相关的 Minerva 指标所有者。
ERF 显示指标元数据,链接到 SLA Tracker[17]从而可视化数据谱系和时间线
总之,Minerva 及其多种集成工具帮助用户能够在他们的计划报告中轻松跟踪指标,测量实验产生的变化,并探索意外的变化——所有这些都让用户相信数据是正确和一致的,这种信心极大的减少了获取洞察所花费的时间,增加了对数据的信任,并有助于支持数据驱动的决策。
尾声
Minerva 引入了一种思考数据的新方法,不仅意味着以业务和指标为中心的用户接口,还需要我们调整传统 BI 工具(主要使用 SQL)来适应 Minerva API 的接口。在某种意义上,这类似于将一个新的方钉(Minerva)插入一个现有的圆孔(BI Tools)中。
随着越来越多的组织接受类似 Minerva 的指标层的理念,我们相信将会有一系列新的挑战等着我们。也就是说,一些开创性的工作肯定会把分析带到新的水平,我们为能够为这一领域做出创新工作而感到自豪,我们也希望会有更多公司跟我们一样在这一领域做出贡献。
感谢
感谢每一个为这篇博文所介绍的工作和成果做出贡献的人[18]。除了之前的致谢,我们还想感谢那些与我们合作,在工作中采用 Minerva 的人。
所有商标都是各自所有者的财产,相关资料的使用仅用于身份识别的目的,并不意味着赞助或背书。
Reference:
[2] https://superset.apache.org/
[5] https://www.r-project.org/
[6] https://www.jstatsoft.org/article/view/v040i01
[7] https://medium.com/airbnb-engineering/supercharging-apache-superset-b1a2393278bd#c576
[9] https://www.elastic.co/elasticsearch/
[10] https://medium.com/airbnb-engineering/supercharging-apache-superset-b1a2393278bd
[11] https://en.wikipedia.org/wiki/Online_analytical_processing
[12] https://pypi.org/project/sqlparse/v
[13] https://calcite.apache.org/avatica/
[14] https://www.sqlalchemy.org/
[15] https://www.python.org/dev/peps/pep-0249/
[16] https://medium.com/airbnb-engineering/visualizing-data-timeliness-at-airbnb-ee638fdf4710
[17] https://medium.com/airbnb-engineering/visualizing-data-timeliness-at-airbnb-ee638fdf4710