一背景
Uber为数十亿次乘车和送货服务提供动力,连接数百万乘客、企业、餐馆、司机和快递员,彻底改变了世界的出行方式。这个庞大的交通平台的核心是大数据和数据科学,它为Uber所做的一切提供动力,例如更好的定价和匹配、欺诈检测、降低预计到达时间等。每天收集和处理数PB的数据,成千上万的用户从这些数据中获得见解并做出决策,以构建/改进这些产品。
二问题
虽然Uber能够扩展其数据系统,但之前没有足够关注一些重要的数据问题,这些问题在规模化时变得更加重要。出现的一些具体问题包括:
数据重复:没有某些关键数据和指标的真实来源,这导致重复、不一致,以及在使用哪些数据和指标时出现很多混乱。消费者必须通过进行大量尽职调查来弥补这一点,从而占用解决业务问题的时间。使用自助服务工具创建的数十万个数据集,没有明显表明哪些数据集很重要,加剧了这个问题。
发现问题:如果没有丰富的元数据和分面搜索,在数十万个数据集中发现数据是很困难的。糟糕的发现会导致重复的数据集、重复的工作以及不一致的答案,具体取决于用于回答问题的数据。
割裂连接的工具:数据流经许多工具、系统和组织。但工具没有相互集成,导致重复工作和糟糕的开发人员体验——例如,必须在多个工具之间复制和粘贴文档和所有者信息;开发人员无法自信地更改架构,因为下游如何使用它并不明显。
日志记录不一致:移动设备上的日志记录是手动完成的;日志没有一个结构来支持一种简单且一致的方式来衡量实际用户行为,从而导致推断用户行为的效率低下且错误。
缺乏流程:跨团队缺乏数据工程流程导致了不同程度的成熟度和变化。跨团队的数据质量和流程的定义或衡量没有一致性。
缺乏所有权和SLA:数据集没有得到适当的拥有——它们通常没有质量保证,错误修复的SLA不一致,随叫随到,事件管理与Uber对服务的管理方式相去甚远。
这些问题并不是Uber独有的——根据我们与其他公司的工程师和数据科学家的访谈,这些都是常见问题,特别是对于那些发展非常快的公司来说。虽然由于故障/损坏的即时可见性,服务和服务质量往往会受到更多关注,但数据和相关工具往往处于次要地位。但修复它们并使它们达到服务工具/管理的严格水平在规模上变得极其重要,特别是如果数据在产品功能和创新中发挥着关键作用,就像Uber那样。
三 解决方法
下图显示了从移动应用程序和服务到数据仓库和最终消费界面的数据流架构图。Uber最初的零碎反应尝试仅在数据流中发生数据问题的点上解决问题,解决的是症状而不是根本原因。Uber意识到需要一种整体方法来解决这些问题并端到端地解决问题。Uber的目标是重组数据记录系统、工具和流程,以实现整个Uber数据质量的阶梯式变化。Uber将整个端到端数据流堆栈的团队聚集在一起,其中包括堆栈各个部分的工程师和数据科学家,最终修改了20多个现有系统。
为了将工作重点放在整体思考上,Uber获取了与Rider应用程序上的行程和会话信息相关的关键数据“片段”,并尝试为它们创建事实来源(SoT),并修复了应用程序上的日志记录。应用程序、处理数据的工具、数据本身以及将它们维护为SoT所需的流程。
四 从第一原则处理数据
与试图隐藏数据并在服务外部暴露狭窄接口的服务不同,仓库中的离线数据更多的是暴露来自相关服务和领域的数据以供一起分析。我们的一个重要认识是,为了做好这件事,我们不仅应该解决数据工具的问题,还应该解决数据的人员和流程方面的问题。因此我们提出了一些指导原则:
数据即代码:数据应被视为代码。数据工件的创建、弃用和关键更改应通过适当的书面文档进行设计审查流程,并考虑消费者的观点。架构更改有强制审核员,他们在更改落地之前签字。模式重用、扩展优于创建新模式。数据工件有与之相关的测试,并且会持续进行测试。这些是我们通常应用于服务API的实践,我们应该将同样的严格性扩展到对数据的思考。
数据有明确所有者:数据就是代码,所有代码都必须拥有。每个数据工件都应该有明确的所有者、明确的目的,并且在其效用结束时应该被弃用。
数据质量是众所周知的:数据工件必须具有数据质量SLA、错误SLA以及事件报告和管理,就像我们为服务所做的那样。所有者有责任维护这些SLA。
提高数据生产力:数据工具的设计必须能够优化生产者和消费者之间的协作,并在必要时配备强制所有者、文档和审阅者。数据工具必须与其他相关工具集成,无缝地绕过必要的元数据。数据工具应满足与服务相同的开发人员等级,提供在实施变更之前编写和运行测试的能力,在投入生产之前在暂存环境中测试变更的能力,以及与现有监控/警报生态系统良好集成的能力。
组织级数据管理:团队的目标应该是配备“全栈”人员,以便拥有必要的数据工程人才来从长远角度看待数据的整个生命周期。虽然复杂的数据集可以由更集中的团队拥有,但大多数生成数据的团队应该以本地所有权为目标。我们应该拥有必要的培训材料,并优先培训工程师,使其相当熟悉基本的数据生产和消费实践。最后,团队领导应对他们生成和使用的数据的所有权和质量负责。
五 经验
下面将重点介绍该计划的一些最有用和最有趣的经验教训。
1.数据质量和等级
由于数据质量差,我们经历了很多辛苦。我们已经看到过这样的例子:实验中不准确的测量导致大量的体力劳动,并导致验证和纠正数据时生产力下降。事实证明,随着大数据的广泛使用,这个问题变得越来越普遍——IBM的一项研究和《哈佛商业评论》估计,由数据驱动的企业会因数据质量差而遭受巨大的负面影响。
为了减少繁琐的工作和负面的业务影响,我们希望开发一种通用语言和框架来讨论数据质量,以便任何人都可以以一致的期望生成或使用数据。为此,我们开发了两个主要概念:标准数据质量检查和数据集层的定义。
(1)数据质量
数据质量是一个复杂的主题,有许多不同的方面值得深入研究,因此我们将数据质量的讨论限制在我们已经取得重大进展的领域,而忽略其他领域以供将来讨论。Uber中数据生成和使用的环境对于我们选择关注哪些数据质量领域发挥着重要作用。虽然其中一些可以转让给其他人,但有些则不能。数据生产者和消费者在Uber中面临的一系列常见问题如下:如何在最新数据分析与完整数据分析之间进行权衡?鉴于我们在不同的数据中心并行运行管道,我们应该如何推理不同数据中心的数据一致性?应该对给定的数据集运行哪些语义质量检查?我们想要选择一组检查来提供推理这些问题的框架。
(2)数据质量检查
经过多次迭代,我们确定了如下所述的五种主要数据质量检查类型。每个数据集都必须进行这些检查并配置默认SLA:
新鲜度:数据生成与数据在目标系统中完成99.9%之间的时间延迟,包括完整性水印(默认设置为3个9),因为简单地优化新鲜度而不考虑完整性会导致质量较差的决策
完整性:目标系统中的行数与源系统中的行数相比的百分比
重复:具有重复主键或唯一键的行的百分比,在原始数据表中默认为0%重复,同时允许在建模表中存在少量重复
跨数据中心一致性:将当前数据中心中的数据集副本与另一个数据中心中的副本进行比较时,数据丢失的百分比
语义检查:捕获数据中字段的关键属性,例如null/not-null、唯一性、不同值的数量以及值的范围
数据集所有者可以选择提供不同的SLA,并为消费者提供适当的文档和推理——例如,根据数据集的性质,人们可能希望牺牲完整性来换取新鲜度。同样,消费者可以选择基于这些指标来使用数据集——基于完整性触发器而不是简单地基于时间触发器来运行管道。
除了上述时间维度的检查之外,我们还将继续在跨数据集概念的一致性和异常检测方面开展更复杂的检查。
(3)数据集层
除了质量衡量之外,还需要有一种方法将数据集与对业务的不同重要性程度关联起来,以便我们可以轻松突出显示最重要的数据。我们已经习惯通过根据数据的业务关键性分配“层级”来为服务做到这一点。这些层有助于确定中断的影响,并提供有关哪些层的数据应用于哪些目的的指南。例如,如果某些数据影响合规性、收入或品牌,则应将其标记为1级或2级。用户为临时探索而创建的不太重要的临时数据集默认标记为第5层,如果不使用,可以在一段固定时间后删除。轮胎还确定必须归档的事件的级别以及用于修复针对数据集创建的错误的SLA。分层的副产品是我们用来做出关键业务决策的数据资产的系统清单。此练习的另一个好处是对相似或不再作为事实来源的数据集进行显式重复数据删除。最后,分层带来的可见性帮助我们重构数据集,以实现更好的建模、一致的数据粒度和标准化水平。
我们开发了自动化功能,为组织生成“分层报告”,显示需要分层的数据集、疲劳数据的使用情况等,作为组织“数据健康状况”的衡量标准。我们还将跟踪这些指标作为我们“工程卓越”指标的一部分。随着更多的采用和反馈,我们不断迭代确切的定义和测量方法,并进一步改进它们。
(4)数据质量工具
如果我们不将它们自动化并使其易于使用和应用,那么仅拥有这些定义是不够的。我们将多个现有的数据质量工具整合为一个实现这些定义的工具。我们在有意义的地方自动生成测试(对于原始数据——将Kafka主题转储到仓库中——我们可以自动生成四类测试,语义测试除外),并可以使用数据集的最少输入轻松创建新测试拥有者。虽然这些标准检查为每个数据集提供了最少的测试集,但该工具的构建也足够灵活,生产者只需提供SQL查询即可创建新测试。我们学到了许多有趣的经验教训,包括如何以低开销扩展这些测试、可以轻松为数据集构建一套测试的抽象、何时安排测试以减少误报和噪音警报、这些测试如何应用于流数据集等。
2.数据手册和元数据
如前所述,Uber拥有数十万个数据集和数千个用户。如果我们考虑其他数据资产——报告、机器学习功能、指标、仪表板等——我们管理的资产数量甚至更大。我们希望确保:a)消费者使用正确的数据做出决策,b)生产者做出明智的决策来发展数据、优先修复错误等。为此,我们需要一个收集有关所有数据的元数据的单一目录资产并根据用户的需求向他们提供正确的信息。事实上,我们意识到,糟糕的发现此前曾导致生产者和消费者产生重复、冗余的数据集,然后被丢弃的恶性循环。
我们希望向用户提供有关每个数据工件(表、列、指标)的全面元数据:
基本元数据:例如文档、所有权信息、管道、生成数据的源代码、示例数据、沿袭和工件的层
使用元数据:有关谁使用它、何时使用、流行查询以及一起使用的工件的统计信息
质量元数据:对数据进行测试,何时运行,哪些通过,并汇总数据提供的SLA
成本元数据:用于计算和存储数据的资源,包括货币成本
Bug和SLA:针对工件、事件、最近警报以及响应所有者问题的总体SLA提交的Bug
创建这个单一元数据目录并提供强大的UI以及上下文感知搜索和发现对于实现生产者和消费者之间的协作、减少使用数据的麻烦以及整体提升数据质量至关重要。
为了实现这一目标,我们彻底改造了内部元数据目录Databook的后端和UI。我们标准化了元数据词汇,使向现有实体添加新的元数据属性变得容易,设计了可扩展性,以最少的入门工作轻松定义新的实体类型,并将我们的大多数关键工具集成到该系统中,并将其元数据发布到这个连接的中心位置各种数据资产、工具和用户之间的点。改进后的用户界面清晰地呈现信息,并支持用户更轻松地过滤和缩小所需数据的范围。这些改进后,工具的使用量急剧增加。
3.应用程序上下文日志记录
为了了解和改进产品,让我们的应用程序日志记录以捕获实际的用户体验至关重要。我们想要衡量用户体验,而不是推断用户体验,但每个团队都有自定义日志记录方法,导致用户体验衡量方式不一致。我们希望标准化整个应用程序中跨团队的日志记录方式,甚至“平台化”日志记录,以便开发人员可以自由地减少考虑捕获所有产品功能所需的日志信息,例如:向用户显示的内容、状态用户与应用程序交互时的应用程序的名称、交互类型和交互持续时间。
在深入研究Uber构建应用程序的移动框架后,我们意识到移动应用程序开发框架已经内置了一个自然结构,可以在用户体验应用程序时提供有关应用程序状态的关键信息。自动捕获RIB的层次结构将为我们提供应用程序的状态以及哪些RIB(大致将它们视为组件)当前处于活动状态。应用程序上的不同屏幕映射到不同的RIB层次结构。
基于这种直觉,我们开发了一个库,可以捕获当前的RIB层次结构,将其序列化,并自动将其附加到从应用程序触发的每个分析事件。在接收这些消息的后端网关中,我们实现了从RIB层次结构到一组灵活的元数据(例如屏幕名称、应用程序中的阶段名称等)的轻量级映射。该元数据可以独立发展,以由生产者或消费者添加更多信息,而无需依赖移动应用程序更改(由于构建和发布周期长达数周,因此速度缓慢且成本高昂)。在后端,除了序列化状态之外,网关还会在写入Kafka之前将此附加元数据附加到分析事件。网关上的映射也可以通过API获得,因此仓库作业可以在映射发生变化时回填数据。
除了上述核心问题之外,我们还必须解决其他一些问题,这里我们不会详细介绍这些问题,例如:优化序列化RIB层次结构以减少分析有效负载大小,提高映射效率,保持映射正确应用程序通过自定义测试框架进行更改,将RIB树映射到正确的状态、标准化屏幕和状态名称等方面存在一些复杂的问题。
虽然这个库并没有完全解决我们想要解决的所有日志记录问题,但它确实提供了一种日志结构,使许多分析变得更加容易,如下所述。我们正在迭代这个库来解决概述的其他问题。
(1)乘客漏斗分析
使用上面的日志框架生成的数据,我们能够大大简化对Rider行为的漏斗分析。我们在几个小时内就构建了一个仪表板,而在过去,这需要花费几周的时间。这些数据目前为许多实验监控和其他仪表板提供支持,以了解用户行为。
(2)公制标准化
当我们开始Data180时,我们公司有许多指标存储库。我们评估了这些解决方案的优缺点,并在名为uMetric的单一存储库上进行了标准化。事实上,它不仅仅是一个存储库-它具有高级功能,例如让用户专注于YAML格式的定义,并通过为Hive/Presto/Spark等不同查询系统生成查询、生成流来消除大量繁琐的工作指标的批量管道、自动创建数据质量测试等。该系统正在得到更广泛的采用,我们也在投资以进一步增强它。我们正在自动执行重复和接近重复的指标检测,将该系统与Databook和其他数据消费界面集成,以便消费者可以只消费指标结果,而不是复制和运行指标的SQL(这样更容易犯错误并通过以下方式重复指标)调整SQL)、改进自助服务性质以及在着陆差异之前检测错误等。这种标准化帮助我们显着减少了使用时的重复和混乱。
4.其他工具和流程变更
除了上面列出的更改之外,我们还实施了其他几项工具和流程更改来改进我们的数据文化,简要描述如下:
(1)共享数据模型:为了避免相同概念的模式定义重复(这种情况很常见),我们改进了模式定义工具,以允许导入和共享现有类型和数据模型。我们现在正在构建额外的功能和流程,以推动共享数据模型的采用,并减少重复和接近重复数据模型的创建。
移动分析强制代码审查者和单元测试:我们重新组织了移动分析事件的架构,并允许生产者和消费者将自己添加为强制审查者,以避免在没有适当审查和通知的情况下推出更改。我们还构建了一个移动日志测试框架,以确保数据测试在构建时运行。
(2)强制所有权:我们在数据生产的根本上改进了数据工具和表面(模式定义、Kafka主题创建、创建数据的管道、指标创建、仪表板创建等),以便在我们无法自动推断所有者时强制执行所有权信息。所有权信息进一步标准化为整个公司的单一服务,并跟踪团队和组织,而不仅仅是个人创建者。此更改消除了新的无主数据。我们进一步运行启发式方法,将所有者分配给没有所有者或不再在公司的所有者的“废弃”数据集,从而使我们有望达到100%的所有权覆盖率。
(3)跨工具集成:我们集成了工具,以便一旦在源工具中设置文档、所有权和其他关键元数据,它就可以无缝地流过所有下游工具。我们将管道工具与标准警报和监控工具集成在一起,因此服务和数据管道的警报生成和管理方式保持一致。
六 展望
我们首先假设全面思考数据,考虑跨人员和系统的完整端到端数据流可以带来更高的整体数据质量。我们相信,这项工作已显示出支持该假设的有力证据。然而,最初的工作仅仅是我们迈向更好的数据文化转型之旅的开始。在这项工作取得成功的基础上,我们将此计划在Uber上推广到不同的组织和应用程序。项目团队专注于分层、构建真实数据源、提高数据质量和数据SLA,而平台团队则继续改进上述工具及其他工具。双方共同努力改进流程,在Uber建立强大的数据文化。正在进行的工作的一些例子包括:
对工具进行更多基础性改进,并提高自动化程度,以支持不同的数据质量检查;更多集成以减少劳累
增强应用程序日志框架,以进一步捕获有关用户在应用程序上实际“看到”和“执行”的内容的更多视觉信息
流程和工具改进以改善生产者和消费者之间的协作
强制执行数据资产的生命周期,以便从我们的系统中删除未使用和不必要的工件
在工程师和数据科学家的日常数据开发工作流程中进一步采用上述原则