PayPal 如何使用 JunoDB 处理每天 3500 亿次请求

虫子会在以下情况下溜走 80% 的用户流程经过测试 在发货之前。但你如何获得这种覆盖范围?你要么花数年时间扩展内部 QA,要么 在 QA Wolf 的帮助下仅用 4 个月就实现了这一目标

怎么样 质量保证狼 不同的?

  • 他们不按小时收费。

  • 他们保证结果。

  • 他们提供运行 15 分钟 QA 周期所需的所有工具和(并行运行)基础设施。

点击此处获取快速演示

您是否曾见过数据库出现故障后又在眨眼间恢复的情况?

PayPal 的 JunoDB 就是一个能够做到这一点的数据库。根据 PayPal 的说法,JunoDB 的可用性可以达到 6 个 9(99.9999%)。这意味着每天的停机时间仅为 86.40 毫秒。

作为参考,我们平均眨眼一次大约需要 100-150 毫秒。

虽然这些统计数据确实令人惊叹,但这也意味着我们可以从 JunoDB 的架构和设计中了解到很多有趣的东西。

在本文中,我们将讨论以下主题:

  • JunoDB 的架构细分

  • JunoDB 如何实现可扩展性、可用性、性能和安全性

  • JunoDB 的用例

在进一步讨论之前,这里有一些有关 JunoDB 的关键事实,可以帮助我们更好地理解它。

  • JunoDB 是一个分布式键值存储。将键值存储视为一本字典,您可以在其中查找单词(“键”)以找到其定义(“值”)。

  • JunoDB 利用用 Go 实现的高度并发架构来高效处理数十万个连接。

  • 在 PayPal,JunoDB 每天处理近 3500 亿个请求,并用于每个核心后端服务,包括登录、风险管理和交易处理等关键功能。

  • PayPal 主要使用 JunoDB 进行缓存,以减少主可信源数据库的负载。不过,还有其他用例,我们将在后面的部分中讨论。

该图显示了 JunoDB 如何融入 PayPal 的整体方案。

关于创建 JunoDB 之类的东西的一个常见问题是:

“为什么 PayPal 不能使用像 Redis 这样的现成产品呢?”

原因是 PayPal 希望数据库支持多核,而 Redis 的设计并不是为了从多个 CPU 核心中获益。它本质上是单线程的,只使用一个核心。通常,如果需要,您需要启动多个 Redis 实例以在多个核心上进行扩展。

顺便说一句,JunoDB 最初是一个单线程 C++ 程序,最初的目标是将其用作内存中的短 TTL 数据存储。

作为参考,TTL 代表生存时间。它指定一段数据应保留的最长持续时间或被视为有效的最大时间。

然而,JunoDB 的目标随着时间的推移而不断发展。

  • 首先,PayPal 希望 JunoDB 能够作为支持长 TTL 的持久数据存储。

  • 其次,JunoDB 还有望默认通过磁盘加密和传输中的 TLS 提供更高的数据安全性。

这些目标意味着 JunoDB 必须受 CPU 限制而不是受内存限制。

作为参考,“内存受限”和“CPU受限”指的是计算机程序中的不同性能方面。顾名思义,内存受限程序的性能受可用内存量限制。另一方面,CPU受限程序取决于CPU的处理能力。

例如,Redis 是内存受限的。它主要将数据存储在 RAM 中,并且针对快速内存访问进行了优化。Redis 性能的限制因素是内存,而不是 CPU。

然而,加密之类的要求是 CPU 密集型的,因为许多加密算法需要原始处理能力来执行复杂的数学计算。

因此,PayPal 决定用 Go 重写早期版本的 JunoDB,使其适合多核并支持高并发。

下图展示了JunoDB的高层架构。

让我们看一下整体设计的主要组成部分。

客户端库是客户端应用程序的一部分,并提供通过 JunoDB 代理存储和检索数据的 API。

它以多种编程语言实现,例如 Java、C++、Python 和 Golang,以便于在不同的应用程序堆栈中使用。

对于开发人员来说,只需选择适合其各自编程语言的库并将其包含在应用程序中以执行各种操作。

JunoDB 采用基于代理的设计,其中代理连接到所有 JunoDB 存储服务器实例。

这种设计有几个重要的优点:

  • 确定哪个存储服务器应该处理查询的复杂性被排除在客户端库之外。由于 JunoDB 是一个分布式数据存储,因此数据分布在多个服务器上。代理负责将请求定向到正确的服务器。

  • 代理还知道存储在 ETCD 键值存储中的 JunoDB 集群配置(例如分片映射)。

但是 JunoDB 代理会成为单点故障吗?

为了防止这种可能性,代理在负载均衡器下游的多个实例上运行。负载均衡器接收来自客户端应用程序的传入请求,并将请求路由到适当的代理实例。

JunoDB 架构中的最后一个主要组件是存储服务器。

这些是接受来自代理的操作请求并将数据存储在内存或持久存储中的实例。

每个存储服务器负责一组分区或分片,以便有效分配数据。

在内部,JunoDB 使用 RocksDB 作为存储引擎。在数据库领域,使用现成的存储引擎(如 RocksDB)很常见,这样可以避免从头开始构建所有内容。作为参考,RocksDB 是一种嵌入式键值存储引擎,针对高读写吞吐量进行了优化。

现在我们已经了解了 JunoDB 的整体设计和架构,现在是时候了解 JunoDB 的一些关键优先事项以及它如何实现这些优先事项了。

几年前,PayPal 转向水平可扩展的基于微服务的架构,以支持活跃客户和支付率的快速增长。

微服务虽然为他们解决了许多问题,但也存在一些缺点。

一个重要的缺点是,由于扩展应用程序层,与键值存储的持久连接数量增加。JunoDB 主要通过两种方式处理此扩展需求。

如前所述,JunoDB 使用基于代理的架构。

如果客户端与数据库的连接达到限制,则可以添加额外的代理来支持更多的连接。

在这种情况下,延迟的权衡是可以接受的。

第二种扩展需求与数据规模的增长有关。

为了确保高效的存储和数据获取,JunoDB 支持基于一致性哈希算法的分区。使用分片映射将分区(或分片)分布到物理存储节点。

在这种情况下,一致性哈希非常有用,因为当集群中的节点由于添加或删除而发生变化时,只有极少量的分片需要重新分配到不同的存储节点。

PayPal 使用固定数量的分片(准确地说是 1024 个分片),并且分片图是预先生成的,存储在 ETCD 存储中。

对分片映射的任何更改都会触发自动数据重新分配过程,从而可以根据需要轻松扩展 JunoDB 集群。

下图更详细地展示了该过程。

高可用性对 PayPal 来说至关重要。全球支付平台一旦瘫痪,声誉将遭受巨大损失。

然而,由于各种原因,例如软件错误、硬件故障、断电,甚至人为错误,都可能出现中断。故障可能导致数据丢失、响应时间变慢或完全不可用。

为了缓解这些挑战,JunoDB 依赖于复制和故障转移策略。

在集群中,JunoDB 存储节点在逻辑上被组织成一个网格。每列代表一个区域,每行代表一个存储组。

数据被划分为分片并分配到存储组。在存储组内,每个分片基于仲裁协议在各个区域之间同步复制。

基于仲裁的协议是分布式数据库中就某个值达成共识的关键。您有两个仲裁:

  • 读经定额组:当客户端想要读取数据时,它需要从一定数量的区域(称为读取仲裁)接收响应。这是为了确保它获取最新的数据。

  • 写入法定人数:当客户端想要写入数据时,它必须收到一定数量的区域的确认,以确保数据被写入大多数区域。

谈到法定人数,有两条重要规则。

  • 读取仲裁和写入仲裁的总和必须大于区域数。如果不是这样,客户端最终可能会读取过时的数据。例如,如果有 5 个区域,其中读取仲裁为 2,写入仲裁为 3,则客户端可以将数据写入 3 个区域,但另一个客户端可能会从尚未收到更新数据的 2 个区域读取数据。

  • 写入仲裁必须大于区域数量的一半,以防止对同一密钥进行两次并发写入操作。例如,如果 JunoDB 集群包含 5 个区域,写入仲裁为 2,则客户端 A 可以将值 X 写入密钥 K,当 2 个区域确认请求时,即视为成功。同样,客户端 B 可以将值 Y 写入同一密钥 K,当两个不同的区域确认请求时,即视为成功。最终,密钥 K 的数据处于不一致状态。

在生产中,PayPal 的配置有 5 个区域、3 个读取仲裁和 3 个写入仲裁。

最后,JunoDB 中的故障转移过程是自动且即时的,无需重新选举领导者或重新分配数据。代理可以通过丢失的连接或超时的读取请求了解节点故障。

跨数据中心复制是通过在不同数据中心的各个集群的代理之间异步复制数据来实现的。

这对于确保即使一个数据中心发生灾难性故障,系统也能继续运行非常重要。

JunoDB 的关键目标之一是提供大规模的高性能。

这意味着在提供出色的用户体验的同时,还能保持个位数毫秒的响应时间。

PayPal 分享的以下图表展示了基准测试结果,证明了 JunoDB 在持久连接和高吞吐量情况下的性能。

作为值得信赖的支付处理商,安全对于 PayPal 来说至关重要。

因此,JunoDB 的设计旨在保护传输中和静止的数据,这并不奇怪。

  • 为了传输安全,客户端和代理之间以及用于复制的不同数据中心的代理之间都启用了 TLS。

  • 有效负载加密在客户端或代理级别执行,以防止对同一数据进行多次加密。理想的方法是在客户端加密数据,但如果没有这样做,代理会通过元数据标志找出答案并执​​行加密。

  • 存储服务器接收并存储在引擎中的所有数据也都经过加密,以确保静态安全。

密钥管理模块用于管理 TLS 证书、会话和加密密钥的分发,以促进密钥轮换,

下图更详细地展示了 JunoDB 的安全设置。

由于 PayPal 已将 JunoDB 开源,因此您也可以在自己的项目中使用它。

JunoDB 可以在各种用例中提供帮助。让我们来看看几个重要的用例:

您可以使用 JunoDB 作为临时缓存来存储不频繁更改的数据。

由于 JunoDB 支持短期和长期 TTL,因此您可以存储从几秒到几天的数据。例如,一个用例是将短期令牌存储在 JunoDB 中,而不是从数据库中获取它们。

您可以在 JunoDB 中缓存的其他项目是用户偏好、帐户详细信息和 API 响应。

您还可以使用JunoDB来实现幂等性。

如果一个操作即使多次应用也能产生相同的结果,则该操作是幂等的。有了幂等性,重复操作就很安全,您无需担心重复付款之类的问题。

PayPal 使用 JunoDB 来确保他们不会因为重试而多次处理某笔付款。JunoDB 的高可用性使其成为理想的数据存储,可以跟踪处理细节而不会使主数据库过载。

假设您有某些资源由于某种原因不可用,或者它们的使用受到访问限制。例如,这些资源可能是数据库连接、API 速率限制或用户身份验证尝试。

您可以使用 JunoDB 存储这些资源的计数器并跟踪它们的使用情况是否超过阈值。

正如我们之前所讨论的,JunoDB 提供了快速的集群间复制。这可以帮助您处理更传统设置中的缓慢复制。

例如,在 PayPal 的案例中,他们以主动-主动模式运行 Oracle,但复制速度通常不如他们希望的那么快。

这意味着如果一个数据中心写入的记录没有在第二个数据中心复制,而第一个数据中心出现故障,那么就有可能出现读取不一致的情况。

JunoDB 可以帮助减少延迟,您可以写入数据中心 A(Oracle 和 JunoDB),即使它出现故障,您也可以从数据中心 B 中的 JunoDB 实例一致地读取更新。

为了更好地理解这个概念,请参见下图。

JunoDB 是一个分布式键值存储,在各种 PayPal 应用程序中发挥着至关重要的作用。它提供高效的数据存储以实现快速访问,从而减轻昂贵的数据库解决方案的负载。

同时,它还满足了可扩展性、高可用性、性能、一致性和安全性等关键要求。

由于其优势,PayPal 已开始在多种用例和模式中使用 JunoDB。对于我们来说,它为我们提供了了解令人兴奋的新数据库系统的绝佳机会。

参考:

Leave a Reply

Your email address will not be published. Required fields are marked *

近期新闻​

编辑精选​