Uber实时数据基础设施:分布式计算架构


Uber 的实时数据基础设施:

  • Apache Kafka 用于流式存储,
  • Flink 用于流处理,
  • Pinot 用于 OLAP,
  • HDFS 用于归档存储,
  • Presto 用于交互式查询

数据来源与挑战
Uber 生成的主要数据来源是其数据中心内的最终用户应用程序,例如 Uber(拼车)和 UberEats。这些数据包括客户端事件和来自优步应用程序中运行的微服务的系统日志。实时数据的生成还来自生产数据库的变更日志,其中正在处理实时事务。对这些数据进行处理以涵盖可以在这三个类别中较高级别涵盖的大量使用。

  • 消息平台
  • 流处理
  • 在线分析处理

构建满足上述用例的数据基础架构所带来的主要挑战是:
  • 随着用户群的增加,正在生成的数据呈指数增长。同样出于持久性和可用性的目的,数据源可以部署在多个位置。数据基础设施需要确保数据的新鲜度并以最小的延迟提供对这些数据的访问。
  • 数据基础设施应该是可扩展的,以适应未来的用例,这些用例可以像多阶段机器学习模型到简单的 SQL 查询接口一样复杂。
  • 随着希望访问和处理数据的用户数量的增加,数据基础设施应该是可扩展的。它还应该为广泛的用户提供工具。因此,能够在 Python 中部署 ML 模型的工程师应该没有问题,而想要查询项目实时财务的财务背景的员工应该能够通过使用类似 SQL 的简单接口来实现它。

数据基础设施的期望是满足广泛的用例,并抽象出捕获和维护来自多个来源的实时数据所涉及的复杂性。


数据基础设施要求
由于数据基础设施必须解决各种各样的用例,因此缩小所有用例中常见的要求非常重要。在所有用例中找到共同点的需求列表如下:

  • 数据需要跨地区保持一致,因为最终用例可能位于无法容忍数据不一致的金融等关键领域。
  • 系统需要高度可用,因为许多实时决策会影响日常运营。动态定价模型依赖于使用来自基础设施的实时数据,并直接影响向客户收取的优步乘车价格。
  • 数据需要是新鲜的,或者换句话说,它应该在生成后的几秒钟内可用。这也会影响 Uber 系统做出正确决策的能力,如果这些决策是通过处理陈旧数据做出的,则会导致业务损失或客户体验不佳。
  • 查询消耗的数据对延迟非常敏感。查询的高延迟将直接影响最终用户的体验。
  • 数据基础设施应该随着 Uber 客户群的增长而扩展。
  • 由于优步是一个低利润的企业,数据基础设施的成本应该尽可能低。
  • 数据基础设施需要为各种各样的用户提供服务,因此它应该公开查询和可编程接口。

有了这么多的用例,不可能在 Uber 的规模上提供高度一致的数据以及可用性。因此,实时基础设施有利于数据的新鲜度而不是数据的一致性。


抽象
数据基础架构带有一组从存储层一直到查询层的抽象。这些抽象的高级概述如下所示:

让我们尝试一次剖析上述抽象中呈现的组件,从存储(从最终用户到 API 和 SQL 的最抽象的组件)到用户直接与数据基础设施交互的 API 和 SQL 的顶层。

  • 存储:存储以通用对象或 blob 格式存储数据。它主要是为写入繁重的用例而不是读取用例而构建的,因为它的作用主要是围绕数据的长​​期存储。OLAP 或流组件直接从存储中读取,以呈现上游用例的数据。
  • 流:这为客户端提供了发布者-订阅者接口。客户端可以订阅一个或多个主题并一次使用一个事件。该组件的首要目标是对数据进行分区并为已发布的事件提供至少一种语义。
  • Compute:它负责对来自流或直接来自存储的事件执行操作。可以使用各种技术来执行这些计算,并且可以使用相同/不同的技术来计算流/存储数据。
  • OLAP:这为来自流或存储的数据提供了有限的 SQL 接口。它针对分析查询进行了优化。
  • SQL:该组件在 OLAP 和计算之上提供了一个成熟的 SQL 接口。在计算之上工作时,SQL 函数被转换为编译函数,并应用于流或存储之上。与 OLAP 交互时,它在 OLAP 查询之上提供附加功能,例如 JOINS。
  • API:该组件为 SQL 接口无法实现的更复杂用例提供可编程接口。
  • 元数据:该组件为管理系统中上述组件的元数据提供支持。

系统总览
最初我们讨论了 Uber 使用多种开源技术来构建他们的数据基础设施。它通过针对我们讨论的用例调整它们来做到这一点,并改进它们以填补空白。作为概述的一部分,我们将一一研究这些技术,以了解它们如何帮助 Uber 构建强大的数据基础设施。


用于流式存储的 Apache Kafka
Kafka是一个著名的开源事件流系统,Uber 目前管理着业内最大的 Kafka 集群之一来构建他们的数据基础设施。在 Uber,Kafka 负责将流数据传输到批处理和实时处理系统。使用案例的范围可以从将事件从驾驶员/骑手应用程序发送到底层分析平台,再到流式数据库更改日志,再到基于这些事件执行计算的订阅者。考虑到各种用例和可扩展性要求,Uber 定制了 Kafka 并添加了一些改进,例如:

  • 集群联合: Uber 开发了一种对集群的抽象,它向生产者/消费者隐藏了集群的内部细节。它是通过向用户公开一个逻辑集群而不是原始集群来完成的。集群联合负责管理与集群相关的元数据并将用户的请求路由到正确的物理集群。这有助于在集群故障期间,因为用户现在不关心他们需要连接到哪个物理集群并更新与其关联的元数据。这反过来又提高了系统的可用性,也通过消除用户过多的集群管理工作量并让他们专注于核心业务逻辑来提高系统的可扩展性。
  • 死信队列:在Kafka中,没有被下游客户端处理的消息要么被要求丢弃(导致数据丢失)要么需要无限期重试(进而影响系统性能并阻塞队列)。这两种方法最终都可能成为数据基础架构中的瓶颈,既不想永久丢失数据,又不想阻止其他消息得到处理。优步通过创建一个死信队列来解决这个问题,在该队列中,在某组重试之后没有被消费的消息被推送到该队列,进而清除队列以处理其他消息。这些消息与死信主题相关联,用户可以根据自己的要求决定稍后处理它们或永久删除它们。
  • 消费者代理:这是对 Kafka 提供的消费者客户端库执行出色封装的另一个示例。由于 Uber 有大量客户端使用来自 Kafka 的消息,因此很难为所有客户端提供更新版本的客户端库。各种编程语言的使用增加了这一挑战。为了克服这个问题,Uber 在来自 Kafka 的客户端库和作为gRPC服务托管的用户消息处理逻辑之间创建了一个层。此抽象还通过将消息移动到死信队列来执行错误处理,因为用户的服务在一组重试后无法使用它。


  • 跨集群复制:由于需要跨区域复制数据以实现容错,这需要跨多个数据中心的Kafka集群复制数据。为了实现这一点,Uber 开发了一个开源复制器,用于在 Kafka 集群之间复制消息,称为uReplicator。为了验证跨集群复制没有数据丢失,Uber 还开发了Chaperone,它充当 Kafka 审计系统,并在检测到数据不匹配时发出警报。

通过上述改进,Uber 能够在 Apache Kafka 之上构建一个可扩展且容错的消息传递平台。

用于流处理的 Apache Flink
为了处理来自 Kafka 的实时数据,Uber 使用构建在Apache Flink之上的流处理系统,这是一个开源流处理框架。Flink 用于面向用户的产品和内部分析。它通过 SQL 接口和可编程 API 向用户公开,用于执行更复杂的工作负载。为了针对 Uber 的用例调整 Flink,在框架之上添加了某些改进。

  • 使用 SQL 构建流分析应用程序: Uber 在 Flink 之上创建了一个名为 FlinkSQL 的 SQL 接口,并将其作为开源项目的一部分。FlinkSQL 让非编程背景的用户只需编写 SQL 查询即可轻松使用流处理。这个接口是一个很好的抽象,它隐藏了使用 Flink 框架的低级 API、作业管理和资源估计的复杂性。现在用户可以专注于他们的业务逻辑,其余的由框架处理。许多工程努力都落后于解决这种抽象带来的挑战。这些挑战中很少有:
  • 资源估计和自动缩放
  • 作业监控和故障自动恢复
  • 部署、管理和操作的统一架构:由于用户由 SQL 和 API 接口提供,因此这两种操作都有一定的功能集。平台层用于在核心 API 之上提供更丰富的功能,它负责将业务逻辑转换为核心 Flink 作业定义,并将其发送到作业管理层进行进一步处理。作业管理层执行作业的验证并管理从部署到监控和故障恢复的整个作业生命周期。存储层托管存储后端和计算集群。

通过这些改进,Flink 已成为 Uber 的核心流处理框架。随着这些改进传递到开源框架,它也使 Flink 社区受益。

用于 OLAP 的 Apache Pinot
Apache Pinot是一个开源的分布式 OLAP 系统,可以对大规模数据执行低延迟查询。它采用分散-收集-合并方法,将大型数据集拆分为段,将查询分解为子计划并并行执行,然后合并结果。 优步将 Pinot 用于各种分析用例,例如实时乘车供需指标和 UberEats 订单统计。它还用于通过为实时数据提供可操作的结果来为许多后端服务器中的决策制定提供动力。优步为 Pinot 做出了贡献,以增强其处理优步独特需求的能力。

  • Upsert 支持: Upsert是单词更新和插入的组合。在数据库世界中,如果指定的值存在,则 upsert 操作将更新现有记录,如果值不存在,则插入新记录。在 Uber,upsert 操作用于更新 UberEats 订单的交付状态或在各种情况下更正乘车费用,例如当乘客更新他们的下车地点时。Uber 在 Apache Pinot 中设计和开发了 upsert 功能,使其成为唯一支持 upsert 功能的开源 OLAP 商店之一。
  • 完整的 SQL 支持:开源 Pinot 版本不支持丰富的 SQL 功能,例如子查询和连接。为了克服这一挑战,Uber 将 Pinot 与Presto(分布式查询引擎)集成在一起,使用户能够在 Pinot 之上使用标准 PrestoSQL 查询。
  • 与数据生态系统的其他部分集成:Pinot 已与 Uber 数据基础设施的其他组件集成,以提供无缝的开发人员体验。此类集成的一些示例包括:
  • 与 Uber 模式服务集成以从 Kafka 主题中自动推断模式
  • 与 FlinkSQL 集成作为数据接收器,允许用户运行 SQL 转换并将结果推送到 Pinot
  • 对等分段恢复:最初,Pinot 严格依赖外部数据存储进行归档,以便从任何类型的故障中恢复。如果档案商店出现故障并反过来完全停止档案过程,这很快就会成为瓶颈。优步通过用异步解决方案替换原始设计来解决这个问题,其中服务器副本可以在故障期间为存档段提供服务。集中式段存储现在被点对点方案取代,从而提高了摄取过程的可靠性和容错性。

再加上上述改进,Apache Pinot 已被 Uber 的数据生态系统广泛采用。与此同时,优步不断投资以改进 Pinot,以更好地应对即将到来的挑战。

用于归档存储的 HDFS
Uber 使用HDFS为其数据基础设施长期存储数据。来自 Kafka 的数据采用 Avro 格式,并作为日志存储在 HDFS 中。这些日志合并为 Parquet 格式,并使用 Hive、Presto 或 Spark 等框架进行处理。这些数据集被视为数据基础架构中的真实来源,并被各种系统用于回填目的。HDFS 还被多个其他系统用于满足其存储需求。
Presto 交互式查询 Presto 是 Facebook 开发的开源、分布式和交互式查询引擎。Presto 的设计目标是对大规模数据进行快速分析查询,并且从某种意义上说它非常灵活,可以很容易地与各种数据源集成。正如我们针对 Pinot 所讨论的,Presto 被数据科学家和工程师通过与 Pinot 集成来对新数据进行探索性分析。

学过的知识
拥有强大且可扩展的数据基础架构为通过探索数据提出新想法并以更好的方式帮助客户提供了可能性。本文中描述的一个此类示例是“UberEats 餐厅经理”的仪表板。该仪表板向餐厅老板提供有关实时客户满意度、热门订单项目等的实时视图,这可以帮助他们实时做出业务决策。拥有这样的仪表板需要访问具有快速查询能力的更新数据,而精心设计的数据基础架构可以满足这一要求。 除了构建满足上述有趣用例的数据架构外,开发过程还伴随着一组可在设计此类基础架构期间使用的学习。其中一些学习是:

  • 开源采用: Uber 的大部分数据基础设施都建立在多种开源技术之上。虽然这确实提供了坚实的基础,但它也伴随着一系列挑战。最大的挑战是大多数开源产品都是为以最佳方式解决特定问题而构建的。而在 Uber,数据基础设施需要解决大量的用例列表,并且可以为未来的用例扩展。这需要 Uber 付出大量的努力,通过投入大量的工程工作来调整和修改这些解决方案以适应他们的用例。其中一个例子是在 Apache Flink 项目之上构建一个完整的 SQL 层。
  • 快速的系统开发和演进:以 Uber 的规模构建数据基础设施需要很多活动。业务需求可能会迅速变化,可能会添加新的需求,并且需要及时纳入新的法规和合规性要求。这需要一个摩擦最小的开发工作流程。Uber 通过标准化客户端接口来实现这一点,这样未来的接口就不会最终破坏客户端代码。瘦客户端也是优步用来高效执行客户端升级的一种非常有效的解决方案。一个例子是构建一个 RESTful Kafka 客户端,其他服务使用它而不是直接使用原始 Kafka 客户端。标准化和适当的抽象有助于 Uber 加快开发人员的工作流程,这有助于应对需求的突然变化。
  • 易于操作和监控:优步在自动化系统部署方面投入了大量资金。他们将这种自动化与对基础设施的各种组件的实时监控相结合,每当检测到系统中的任何差异时都会发出警报。
  • 易于用户入职和调试:随着数据基础设施的使用规模,有必要为用户提供正确的工具集,用于入职和调试典型的工作流特定问题。数据发现和数据审计等技术允许用户发现他们正在寻找的数据,并在公司范围内检测数据问题。每当新服务上线时,基础设施也会自动配置 Kafka 主题,并随着使用量的增加配置资源。这为用户提供了无缝的入职体验,因此他们可以专注于他们的用例,而不是弄清楚基础设施的复杂性。

我们已经看到在构建强大且可扩展的数据基础架构以支持 Uber 规模的组织的数据需求方面付出了多少努力。这些工作包括了解需求及其扩展的范围、利用开源工具使您无需从头开始构建每个组件、针对您的用例修改这些工具以及创建一个生态系统,您的用户可以利用该生态系统构建数据解决方案来解决问题复杂的业务问题。