Minerva -- Airbnb的大规模数据指标系统 Part 3

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