从司机与乘客的位置和目的地,到餐馆的订单和支付交易,Uber 的运输平台上的每次互动都是由数据驱动的。数据赋能了 Uber 的全球市场,使咱们面向全球乘客、司机和食客的产品得以具有更可靠、更无缝的用户体验,并使咱们本身的员工可以更有效地完成工做。html
凭借系统的复杂性和数据的普遍性,Uber 将数据驱动提高到了一个新的水平:天天处理万亿级的 Kafka 消息,横跨多个数据中心的 HDFS 中存储着数百 PB 的数据,并支持每周上百万的分析查询。前端
然而大数据自己并不足以造成看法。Uber 规模的数据要想被有效且高效地运用,还须要结合背景信息来作业务决策,这才能造成看法。为此咱们建立了 Uber 的内部平台 Databook,该平台用于展现和管理具体数据集的有关内部位置和全部者的元数据,从而使咱们可以将数据转化为知识。mysql
自 2016 年以来,Uber 的平台上已增长了多项新业务,包括 Uber Eats、Uber Freight,以及 Jump Bikes。现在,咱们天天至少完成 1500 万次行程,每个月活跃有超过 7500 万乘客。在过去的八年中,Uber 已从一家小型创业公司发展到在全球拥有 18,000 名员工。android
随着这种增加,数据系统和工程架构的复杂性也增长了。例若有数万张表分散在多个在役的分析引擎中,包括 Hive、Presto 和 Vertica。这种分散致使咱们急需了解信息的全貌,特别是咱们还在不断添加新业务和新员工。 2015 年的时候,Uber 开始手动维护一些静态 HTML 文件来对表进行索引。ios
随着公司发展,要更新的表和相关元数据的量也在增加。为确保咱们的数据分析能跟上,咱们须要一种更加简便快捷的方式来作更新。在这种规模和增加速度下,有个能发现全部数据集及其相关元数据的强大系统简直不要太赞:它绝对是让 Uber 数据能利用起来的必备品。git
图 1:Databook 是 Uber 的内部平台,能够展现和管理数据有关内部位置和全部者的元数据。github
为使数据集的发现和探索更容易,咱们建立了 Databook。 Databook 平台管理和展现着 Uber 中丰富的数据集元数据,使咱们员工得以探索、发现和有效利用 Uber 的数据。 Databook 确保数据的背景信息 —— 它的意义、质量等 —— 能被成千上万试图分析它的人接触到。简而言之,Databook 的元数据帮助 Uber 的工程师、数据科学家和运营团队将此前只能干看的原始数据转变为可用的知识。sql
经过 Databook,咱们摒弃了手动更新,转为使用一种先进的自动化元数据库来采集各类常常刷新的元数据。Databook 具备如下特性:数据库
Databook 提供了各类各样的元数据,这些元数据来自 Hive、Vertica、MySQL、Postgres、Cassandra 和其余几个内部存储系统,包括:apache
全部的元数据均可以经过一个中心化的 UI 和 RESTful API 来访问到。 UI 使用户能够轻松访问到元数据,而 API 则使 Databook 中的元数据能被 Uber 的其余服务和用例使用。
虽然说当时已经有了像 LinkedIn 的 WhereHows 这种开源解决方案,但在 Databook 的开发期间,Uber 尚未采用 Play 框架和 Gradle(译者注:二者为 WhereHows 的依赖)。 且 WhereHows 缺少跨数据中心读写的支持,而这对知足咱们的性能需求相当重要。所以,利用 Java 自己强大的功能和成熟的生态系统,咱们建立了本身内部的解决方案。
接下来,咱们将向您介绍咱们建立 Databook 的过程以及在此过程当中咱们遇到的挑战。
Databook 的架构能够分为三个部分:采集元数据、存储元数据以及展现元数据。下面图 2 描绘的是该工具的总体架构:
图 2:Databook 架构:元数据从 Vertica、Hive 和其余存储系统中获取,存储到后端数据库,经过 RESTful API 输出。
Databook 引入多个数据源做为输入,存储相关元数据并经过 RESTful API 输出(Databook 的 UI 会使用这些 API)。
在初次设计 Databook 时,咱们就必须作出一个重大的决定,是事先采集元数据存起来,仍是等到要用时现去获取?咱们的服务须要支持高通量和低延迟的读取,若是咱们将此需求托付给元数据源,则全部元数据源都得支持高通量和低延迟的读取,这会带来复杂性和风险。好比,获取表模式的 Vertica 查询一般要处理好几秒,这并不适合用来作可视化。一样,咱们的 Hive metastore 管理着全部 Hive 的元数据,令其支持高通量读取请求会有风险。既然 Databook 支持许多不一样的元数据源,咱们就决定将元数据存储在 Databook 自身的架构中。此外,大多数用例虽然须要新鲜的元数据,但并不要求实时看到元数据的更改,所以按期爬取是可行的。
咱们还将请求服务层与数据采集层分开,以使二者能运行在独立的进程中,以下面的图 3 所示:
图 3:Databook 由两个不一样的应用层组成:数据采集爬虫和请求服务层。
两层隔离开可减小附带影响。例如,数据采集爬虫做业可能占用较多的系统资源,没隔离就会影响到请求服务层上 API 的 SLA。另外,与 Databook 的请求服务层相比,数据采集层对中断不太敏感,若是数据采集层挂掉,可确保仍有以前的元数据能提供,从而最大限度地减小对用户的影响。
咱们的下一个挑战是肯定如何最且成效且最高效地从多种不一样数据源采集元数据。咱们考虑过多种方案,包括建立一个分布式的容错框架,利用基于事件的数据流来近乎实时地检测和调试问题。
咱们先建立了爬虫来按期采集各类数据源和微服务生成的有关数据集的元数据信息,例如表的使用数据统计,它由咱们用于解析和分析 SQL 的强大开源工具 Queryparser 生成。(顺带一提:Queryparser 也由咱们的“数据知识平台”团队建立)。
咱们须要以可扩展的方式频繁采集元数据信息,还不能阻塞到其余的爬虫任务。为此,咱们将爬虫部署到了不一样的机器,这就要求分布式的爬虫之间能进行有效协调。咱们考虑配置 Quartz 的集群模式(由 MySQL 支持)来作分布式调度。可是,却又面临两个实现上的障碍:首先,在多台机器上以集群模式运行 Quartz 须要石英钟的按期同步,这增长了外部依赖,其次,在启动调度程序后咱们的 MySQL 链接就一直不稳定。最后,咱们排除了运行 Quartz 集群的方案。
可是,咱们仍然决定使用 Quartz,以利用其强大的内存中调度功能来更轻松、更高效地向咱们的任务队列发布任务。对于 Databook 的任务队列,咱们用的是 Uber 的开源任务执行框架 Cherami。这个开源工具让咱们能在分布式系统中将消费程序解耦,使其能跨多个消费者群组进行异步通讯。有了 Cherami,咱们将 Docker 容器中的爬虫部署到了不一样的主机和多个数据中心。使用 Cherami 使得从多个不一样来源采集各类元数据时不会阻塞任何任务,同时让 CPU 和内存的消耗保持在理想水平并限制在单个主机中。
尽管咱们的爬虫适用于大多数元数据类型,但有一些元数据还须要近乎实时地获取,因此咱们决定过渡到基于 Kafka 的事件驱动架构。有了这个,咱们就能及时检测和调试数据中断。咱们的系统还能够捕获元数据的重大变更,例如数据集上下游关系和新鲜度,以下面的图 4 所示:
图 4:在 Databook 中,对每一个表采集上下游关系/新鲜度元数据。
这种架构使咱们的系统可以以程序方式触发其余微服务并近乎实时地向数据用户发送信息。但咱们仍需使用咱们的爬虫执行诸如采集/刷新样本数据的任务,以控制对目标资源的请求频率,而对于在事件发生时不必定须要采集的元数据(好比数据集使用状况统计)则自动触发其余系统。
除了近乎实时地轮询和采集元数据以外,Databook UI 还从使用者和生产者处采集数据集的说明、语义,例如表和列的描述。
在 Uber,咱们的大多数数据管道都运行在多个集群中,以实现故障转移。所以,同一张表的某些类型的元数据的值(好比延迟和使用率)可能因集群的不一样而不一样,这种数据被定义为集群相关。相反,从用户处采集的说明元数据与集群无关:描述和全部权信息适用于全部集群中的同一张表。 为了正确关联这两种类型的元数据,例如将列描述与全部集群中的表列相关联,能够采用两种方案:写时关联或读时关联。
将集群相关的元数据与集群无关的元数据相关联时,最直接的策略是在写入期间将元数据关联在一块儿。例如,当用户给某个表列添加描述时,咱们就将信息保存到全部集群的表中,以下面的图 5 所示:
图 5:Databook 将集群无关的元数据持久化保存到全部表中。
这方案可确保持久化数据保持整洁。例如在图 5 中,若是“列 1”不存在,该集群就会拒绝该请求。但这存在一个重要的问题:在写入时将集群无关的元数据关联到集群相关的元数据,全部集群相关的元数据必须已经存在,这在时间上只有一次机会。例如,当在图 5 中改动表列描述时,还只有集群 1 有此“列 1”,则集群 2 的写入失败。以后,集群 2 中同一个表的模式被更新,但已错失机会,除非咱们按期重试写入,不然此描述将永远不可用,这致使系统复杂化。下面图 6 描述了这种状况:
图 6:Databook 将集群无关的元数据持久保存到全部表中。
实现目标的另外一种方案是在读取时关联集群无关和集群相关的元数据。因为这两种元数据是在读取时尝试关联,无所谓集群相关的元数据一时是否存在,所以这方案能解决写时关联中丢失元数据的问题。当表模式更新后显示“列 1”时,其描述将在用户读取时被合并,以下面图 7 所示:
图 7:Databook 在读取时关联集群相关和集群无关的元数据。
Databook 后端最初是使用 MySQL,由于它开发速度快,能够经过 Uber 的基础设施自动配置。可是,当涉及多数据中心支持时,共享 MySQL 集群并不理想,缘由有三:
出于这些缘由,咱们选择 Cassandra 来取代 MySQL,由于它具备强大的 XDC 复制支持,容许咱们从多个数据中心写入数据而不会增长延迟。并且因为 Cassandra 具备线性可扩展性,咱们再也不须要担忧适应 Uber 不断增加的数据量。
Databook 提供了两种访问元数据的主要方法:RESTful API 和可视化 UI。Databook 的 RESTful API 用 Dropwizard(一个用于高性能 RESTful Web 服务的 Java 框架)开发,并部署在多台计算机上,由 Uber 的内部请求转发服务作负载平衡。
在 Uber,Databook 主要用于其余服务以程序方式访问数据。例如,咱们的内部查询解析/重写服务依赖于 Databook 中的表模式信息。API 能够支持高通量读取,而且能够水平扩展,当前的每秒查询峰值约为 1,500。可视化 UI 由 React.js 和 Redux 以及 D3.js 编写,主要服务于整个公司的工程师、数据科学家、数据分析师和运营团队,用以分流数据质量问题并识别和探索相关数据集。
搜索是 Databook UI 的一项重要功能,它使用户可以轻松访问和导航表元数据。咱们使用 Elasticsearch 做为咱们的全索引搜索引擎,它从 Cassandra 同步数据。以下面图 8 所示,使用 Databook,用户可结合多个维度搜索,例如名称、全部者、列和嵌套列,从而实现更及时、更准确的数据分析:
图 8:Databook 容许用户按不一样的维度进行搜索,包括名称、全部者和列。
经过 Databook,Uber 如今的元数据比以往更具可操做性和实用性,但咱们仍在努力经过建造新的、更强大的功能来扩展咱们的影响力。咱们但愿为 Databook 开发的一些功能包括利用机器学习模型生成数据看法的能力,以及建立高级的问题检测、预防和缓解机制。
若是结合内部和开源解决方案创建可扩展的智能服务并开发有创意的复杂技术对您来讲有吸引力,请联系 Zoe Abrams(za@uber.com)或申请咱们团队的职位!
若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。