数据赋能 Uber
Uber 通过赋能数十亿打车和快递服务,连接数以百万计的乘客、企业、餐馆、司机和快递员,彻底改变了世界的出行方式。这个庞大的交通平台的核心是大数据和数据科学,它们支撑着 Uber 的所有工作,比如更好的定价和匹配、欺诈检测、降低预计达到时间(ETA)和实验。每天 PB 级的数据被收集和处理,成千上万用户根据这些数据进行分析决策,从而构建/改进这些产品。
规模扩展带来的问题
虽然我们能够扩展我们的数据系统,但以前,对于一些重要的数据问题,我们没有给予足够的关注,在规模扩大之后,它们变得更加重要,涉及的具体问题包括:
数据重复: 一些关键数据和指标缺少一个真实的数据来源,这导致了重复、不一致,并且在使用时会有很多困惑。消费者必须从解决业务问题中抽出时间来做大量的尽职调查,从而弥补这一点。使用自助服务工具创建的数十万个数据集加剧了这个问题,因为我们无法明显看出哪个数据集更重要。
发现问题: 如果没有丰富的元数据和分面搜索,在数十万数据集中发现数据是很困难的。糟糕的发现导致了重复的数据集、重复的工作和不一致的答案(这取决于回答问题时所使用的数据)。
**工具互不连通:**数据流经许多工具、系统和组织。但是我们的工具没有相互集成,导致工作重复和糟糕的开发体验——例如,必须在多个工具之间复制粘贴文档和所有者信息;开发者无法自信地修改数据模式,因为不清楚它在下游是如何使用的。
日志不一致: 在移动设备上的日志是手动完成的;日志没有统一的结构,我们无法通过简单、一致的方法度量用户的实际行为,只能通过推断来判定(这低效且容易出错)。
流程缺失: 缺乏跨团队的数据工程流程,导致各个团队的成熟度不同,团队间没有一致的数据质量定义或指标。
所有权和 SLA 缺失: 数据集没有明确的属主——它们通常没有质量保证、错误修复的 SLA 不一致,电话支持、事件管理与我们对服务的管理方式相去甚远。
这些问题并不是 Uber 独有的——根据我们与其它公司的工程师和数据科学家的交流,这些问题很常见,特别是对于那些增长非常快的公司。由于服务故障/中断即时可见,所以我们对服务和服务质量的关注比较多,而对数据和相关工具的关注往往比较少。但在规模比较大时,解决这些问题,并使其与服务工具/管理的严格程度保持一致,变得极其重要,尤其是如果数据在产品功能和创新中扮演着关键角色,正如数据在 Uber 的角色一样。
需要一种整体的数据解决方案
下图显示了从移动应用程序和服务到数据仓库和最终消费面的高级数据流。我们最初只在数据流中出现数据问题的地方应急性地解决了这些问题的症状,而没有解决根本问题。我们认识到,需要一种整体的方案来解决这些问题,并彻底解决其根源。我们的目标是重新组织数据日志系统、工具和流程,从而逐步改变整个 Uber 的数据质量。我们召集了横跨端到端数据流堆栈的团队,其中包括来自堆栈各个部分的工程师和数据科学家,最终修改了 20 多个现有系统。
为了将精力集中在整体思考上,我们从 Rider 应用程序上获取了与行程和会话信息相关的关键数据“切片”,并尝试为它们构建一个真实数据源(source of truth,SoT),并且修复应用程序上的日志记录、数据处理工具、数据本身以及将其维护成 SoT 所需的过程。
数据处理的基本原则
与试图隐藏数据并向服务外部公开狭窄接口的服务不同,仓库中的离线数据更多的是公开来自相关服务和领域的数据,以便一起进行分析。我们的一个关键认识是,为了做好这一点,我们不仅要解决数据工具的问题,还要解决数据的人员和流程方面的问题。因此我们提出了一些指导原则:
数据即代码: 数据应该作为代码对待。对数据工件的创建、弃用和关键更改应该通过设计评审流程,并使用适当的书面文档,而且是以客户视角编写的文档。必须为模型更改指定审阅者,他们在更改落地之前进行评审。模型复用/扩展优先于创建新模型。数据工件具有与之相关联的测试,我们要对其进行持续测试。通常,这是用于 API 服务的实践,在考虑数据时,我们需要同样严格。
数据要有属主: 数据就是代码,所有的代码必须有属主。每个数据工件必须有一个明确的所有者,一个明确的目的,并且在用完后废弃。
数据质量可知: 数据工件必须有数据质量 SLA,以及事件报告和管理,就像我们对服务所做的那样。所有者负责维护这些服务协议。
加速数据生产效率: 数据工具的设计必须可以改进生产者和消费者之间的协作,必要时必须有所有者、文档和审阅者。数据工具必须与其它相关工具无缝集成,让我们不必再考虑必要的元数据。数据工具应配备与服务相同级别的开发人员,提供在更改落地之前编写和运行测试的能力,在转入生产环境之前在准生产环境中测试这些更改的能力,并与现有的监控/告警生态系统很好地集成。
针对数据的组织: 团队的目标应该是“全栈”配置,因此,必要的数据工程人才就可以对数据的整个生命周期进行长期的观察。虽然更为核心的团队有复杂的数据集,但是大多数生成数据的团队应该以本地所有权为目标。我们应该有必要的培训材料,并优先培训工程师,使其相当精通数据的生产和消费实践。最后,团队领导应该对他们生产和使用的数据的所有权和质量负责。
已经解决的问题
在本文的余下部分,我们将重点介绍我们编程体验中一些最有用也最有趣的收获。
数据质量和等级
由于数据质量差,我们经受了许多艰苦的工作。我们看到过这样的例子:实验测量值不准确,导致了大量的体力劳动,降低了验证和修正数据的效率。事实证明,随着大数据的应用,这个问题变得越来越普遍——据IBM的一份研究和哈佛商业评论(HBR)估计,由数据驱动的企业将因数据不足而遭受巨大的负面影响。
为了减少繁重的工作和不良的业务影响,我们希望开发一种用于讨论数据质量的通用语言和框架,以便任何人都可以以一致的预期来生产和消费数据。为了实现这一点,我们发展了两个主要概念:标准数据质量检查和数据集等级定义。
数据质量
数据质量是一个复杂的话题,有许多不同的方面值得深入研究,因此我们将把数据质量的讨论局限于我们已经取得重大进展的领域,而将其它方面留待以后讨论。Uber 生成和使用数据的环境对我们选择关注哪些数据质量领域起着重要的作用。虽然其中有些也能适用于其他人,但有些不是。在 Uber,数据生产者和消费者面临的一系列常见问题是:如何在分析最新数据和完整数据之间进行权衡?如果在不同的数据中心并行运行管道,我们如何解释不同数据中心的数据一致性?在给定的数据集上应该运行什么语义质量检查?我们想要选择一组检查,为解释这些问题提供一个框架。
数据质量检查
经过几次迭代,我们得到了下面描述的 5 种主要类型的数据质量检查。每个数据集都必须附带这些检查并配置默认的 SLA:
新鲜度: 从数据生成到数据在目标系统中达到 99.9%完成度之间的时间延迟,包括完整性水印(默认设置为 39 秒),因为只优化新鲜度而不考虑完整性会导致低质量的决策。
完整性: 目标系统中的行数与源系统中的行数之比。
重复: 具有重复主键或唯一键的行数百分比,在原始数据表中默认为 0%重复,而在建模表中允许少量重复。
跨数据中心的一致性: 将当前数据中心中的数据集副本与另一个数据中心中的副本进行对比时,数据丢失的百分比。
语义检查: 捕获数据字段的关键属性,例如 null/not-null、唯一性、不同值的百分比和值的范围。
数据集所有者可以选择提供不同的 SLA,并向消费者提供适当的文档和解释——例如,根据数据集的性质,人们可能想要牺牲完整性来换取新鲜度(比如流式数据集)。相似地,消费者可以选择基于这些指标来消费数据集——基于完整性触发器而不是简单地基于时间触发器来运行管道。
我们正继续研究更复杂的检查,包括跨数据集概念的一致性,以及在上述时间维度检查的基础上进行异常检测。
数据等级
除了质量度量之外,拥有一种将数据集与业务不同等级的重要性关联起来的方法也是必要的,这样就很容易突出显示最重要的数据。对于服务,我们就是通过分配“等级”(基于数据的业务重要性)实现这一点的。这些等级有助于确定停机的影响,并提供了关于哪些数据等级应用于哪些目的的指导原则。例如,如果某些数据影响合规性、收入或品牌,那么它应该被标记为第 1 级或第 2 级。由用户创建的用于不太重要的临时搜索的临时数据,默认标记为第 5 级,如果不使用则可以在一段固定时间后删除。数据等级还确定提交的事件的级别,以及针对数据集创建的修复 bug 的 SLA。分级的一个副产品是数据资产的系统性清单,我们依赖这些资产来做业务关键决策。这样做的另一个好处是,对相似或不再作为事实来源的数据集进行显式去重。最后,通过分级实现的可见性有助于我们重构数据集,从而可以改进建模、数据粒度一致性和规范化级别。
我们已经开发了自动化方法来为机构生成“分级报告”,显示需要分级的数据集、分级数据的使用情况等,作为机构的“数据健康”的衡量指标。我们还跟踪这些指标作为“工程卓越性”标准。随着越来越多的采用和反馈,我们不断迭代具体的定义和度量方法,进一步改进它们。
数据质量工具
如果我们不将这些定义自动化并使其易于使用和应用,仅仅拥有这些定义是不够的。我们将多个现有的数据质量工具整合到一个实现了这些定义的工具中。如果有意义,我们可以自动生成测试(对于原始数据,即由 Kafka 主题转储到数据仓库的数据,除了语义测试之外,我们还可以自动生成四类测试),并且通过最小化数据集所有者的输入简化了测试创建过程。这些标准检查为每个数据集提供了一个最小测试集,同时,该工具也为生产者提供了足够的灵活性,他们只需提供一个 SQL 查询就可以创建新的测试。我们学到了许多有趣的经验,包括如何以较低的开销扩展这些测试,如何简化为数据集构建一套测试的抽象,何时调度测试以减少误报和噪音警报,如何将这些测试应用于流式数据集,以及我们希望在以后的文章中发布的更多内容。
Databook 和元数据
如前所述,我们有成千上万的数据集和成千上万的用户。如果我们考虑其它数据资产——报表、机器学习特征、度量、仪表盘——我们管理的资产数量会大得多。我们希望确保:a)消费者使用正确的数据做出决策,b)生产者做出明智的决策以改进数据、确定错误修复的优先级等。为此,我们需要一个单一的目录,该目录收集所有数据资产的元数据,并根据用户的需求向他们提供正确的信息。事实上,我们意识到,糟糕的发现曾导致生产者和消费者产生重复、冗余数据集的恶性循环,然后这些数据集就被废弃了。
我们希望向用户提供关于每个数据工件(表、列、度量)的详细元数据:
基础元数据: 例如文档、所有权信息、管道、生成数据的源代码、示例数据、谱系和工件层
使用元数据: 关于什么人在什么时候使用这些元数据、流行查询和一起使用的工件的统计
质量元数据: 对数据进行测试,何时运行、哪些测试通过,以及数据提供的聚合 SLA
成本元数据: 用于计算和存储数据的资源,包括货币成本
Bug 和 SLA: 针对工件、事件、近期预警和总体 SLA 提交的 bug,从而响应所有者的问题
创建这个单一的元数据目录并提供一个功能强大的用户界面(具有基于上下文的搜索和发现功能),对于实现生产者和消费者之间的协作、减少使用数据的工作量以及提升总体数据质量至关重要。
为了实现这个目标,我们对内部元数据目录 Databook 的后端和 UI 进行了彻底的改进。我们对元数据词汇表进行了标准化,使其易于向现有实体添加新的元数据属性,设计了扩展性,以便以最小的工作量轻松定义新的实体类型,并将我们的大多数关键工具集成到该系统中,并将它们的元数据发布到这个中心位置,把各种数据资产、工具和用户连接起来。改进后的用户界面更清晰,并且用户可以更方便地过滤和缩小所需数据的范围。经过这些改进后,工具使用量急剧增加。我们在这篇博客中详细介绍了这些变化:Turning Metadata Into Insights with Databook。
应用程序上下文日志
为了了解和改进产品,让我们的应用程序打印日志来获取实际的用户体验是至关重要的。我们希望测量用户体验,而不是推断用户体验,但是每个团队都有一个自定义的日志打印方法,导致在如何测量用户体验方面存在不一致。我们希望标准化整个应用系统中各个团队的日志打印方式,甚至“平台化”日志打印,这样开发者就可以在开发所有产品功能时免于去考虑如何通过日志打印必需信息,例如:向用户展示了什么、与用户交互时应用程序的状态、交互类型和交互持续时间。
在深入研究了 Uber 用来构建应用程序的移动框架之后,我们意识到,移动应用开发框架(之前是开源的)已经内置了一个天然的结构,当用户交互时,可以提供有关应用程序状态的关键信息。自动获取RIB层次将使我们了解应用程序的状态,以及哪个 RIB(大致可以将它们当作组件)当前是活跃的。应用程序上的不同屏幕映射到不同的 RIB 层次。
基于这种直觉,我们开发了一个库来捕获当前的 RIB 层次,将其序列化,并且自动将其附加到应用程序触发的每个分析事件。在接收这些信息的后端网关中,我们实现了从 RIB 层次结构到一组灵活的元数据(例如屏幕名称、应用程序中阶段的名称,等等)的轻量级映射。这些元数据可以独立演化,生产者或消费者都可以添加更多信息,而不必依赖移动应用程序的更改(由于构建和发布周期长达数周,因此更改速度慢且成本高)。在后端,网关在写入 Kafka 之前,除了序列化状态之外,还会将这些额外的元数据附加到分析事件中。网关上的这个映射也可以通过 API 获得,这样仓库作业就可以在映射演进时回填数据。
除了上面的核心问题之外,我们还必须解决一些其它问题,这里我们将不再详细介绍,例如:优化序列化的 RIB 层次结构来减少分析负载大小,使映射高效,通过自定义测试框架在应用程序更改时保持映射正确,将 RIB 树正确映射到状态,对屏幕和状态名称进行标准化,等等。
虽然这个库并没有完全解决我们要解决的所有日志问题,但它确实为日志提供了一个结构,使许多分析变得更容易,如下所述。我们正在迭代这个库来解决提出的其它问题。
骑手漏斗分析
使用上面的日志框架生成的数据,我们能够极大地简化对骑手行为的漏斗分析。我们在几个小时内就建立了一个仪表盘,这在过去可能需要几个星期的时间。这些数据目前正在为许多实验监控和其它仪表盘提供支持,让我们可以了解用户行为。
度量标准化
当我们启动 Data180 时,公司中有许多度量代码库。我们评估了这些解决方案的优缺点,并在一个名为 uMetric 的代码库上进行了标准化。事实上,它不仅仅是一个代码库——它具有高级功能,例如让用户专注于 YAML 格式的定义,并通过为不同的查询系统(例如 Hive/Presto/Spark)生成查询、为度量生成流式处理和批处理管道、自动创建数据质量测试等省去了大量的工作。这个系统正在得到更广泛的采用,我们也在投资进一步加强它。我们正在自动化重复和近似重复的度量检测,将此系统与 Databook 和其它数据消费界面集成,这样消费者就可以直接消费度量结果,而不是复制和运行度量 SQL(调整 SQL 更容易出错及导致度量重复),改进了自助服务的性质,并在事故发生之前检测到错误,等等。这种标准化帮助我们大大减少了消费时的重复和混乱。这个系统在这个博客中有详细的描述——The Journey Towards Metric Standardization。
其它工具和流程变更
除了上面列出的更改之外,我们还实施了其它几个工具和流程更改,以改进我们的数据文化,简要介绍如下:
共享数据模型: 为了避免重复定义相同概念的模式(这很常见),我们改进了模式定义的工具,允许导入和共享现有类型和数据模型。我们正在构建其它特性和流程,从而推动共享数据模型的采用,减少重复和近似重复的数据模型的创建。
移动分析强制代码评审者和单元测试: 我们重新组织了移动分析事件的模式,并允许生产者和消费者将自己添加为强制评审者,以避免在没有适当评审和通知的情况下进行更改。我们还构建了一个移动日志测试框架,以确保数据测试在构建时运行。
强制所有权: 我们改进了数据生成的底层数据工具和界面(模式定义、Kafka 主题创建、创建数据的管道、度量创建、仪表盘创建,等等),以便在无法自动推断所有者时强制提供所有权信息。所有权信息进一步标准化为整个公司的单一服务,跟踪团队和组织,而不仅仅是单个创建者。这一修改可以避免新增无主数据。我们进一步运行启发式算法,将所有者分配到“废弃”的数据集,这些数据集没有所有者或所有者已不在公司,这使我们有望达到 100%的所有权覆盖率。
跨工具集成: 我们集成了工具,这样一旦在源工具上设置了文档、所有权和其它关键元数据,它就无缝地跨所有下游工具流动。我们将管道工具与标准告警和监控工具集成在一起,使服务和数据管道在生成和管理告警方面具有一致性。
后续工作
我们从这样一个假设开始:对数据进行全面的思考——考虑跨人员和系统的端到端数据流——可以提高整体数据质量。我们认为,这一努力已经显示出支持这一假设的有力证据。然而,最初的工作仅仅是我们向更好的数据文化转型之旅的开始。在这项工作取得成功的基础上,我们将这个程序推广到 Uber 不同的组织和应用程序。项目团队专注于分级、构建真实数据源、提高数据质量和数据 SLA,而平台团队则继续改进上述以及更多的工具。双方共同努力来改进流程,在 Uber 建立一种强大的数据文化。举例来说,下面是一些正在进行中的工作:
对工具进行了更多的基础性改进,实现了更多的自动化以支持不同的数据质量检查;实现了更多的集成以减少工作量
增强应用程序日志框架,进一步捕获更多有关用户在应用程序上实际“看了什么”和“做了什么”的直观信息
改进流程和工具,改善生产者和消费者之间的协作
对数据资产实施生命周期管理,以便从系统中移除未使用和不必要的工件
在工程师和数据科学家的日常数据开发流程中进一步应用上述原则
我们希望在未来走向更好的数据文化的过程中分享更多的经验教训。如果你有兴趣在 Uber 解决具有挑战性的数据问题,请在这里或[这里](https://www.uber.com/us/en/careers/list/?query=Engineering - Data&department=Engineering&team=Data&fileGuid=9DPRRvD9vRXQrPCv)申请。
作者介绍
Krishna Puttaswamy是 Uber 高级工程师。他在市场团队中处理各种数据和实验问题。这篇博客中描述的工作,是他在应用数据改进 Uber 应用程序和服务时所面临的实际问题的解决方案。他目前领导 DataNG 和一个重写实验平台的项目。他以前在 Airbnb 和 LinkedIn 处理过数据/机器学习问题。
Suresh Srinivas是一位主要致力于数据平台的架构师,专注于让用户成功地从 Uber 的数据中实现价值。这篇博客中描述的工作,是这一努力的一部分。在 Uber 之前,他联合创建了 Hortonworks,这是一家围绕 Apache 开源项目建立的公司,旨在将 Hadoop 生态系统引入企业。Suresh 是 Apache Hadoop 和相关项目的长期贡献者,也是 Hadoop PMC 成员之一。
原文链接
https://eng.uber.com/ubers-journey-toward-better-data-culture-from-first-principles/
评论 2 条评论