龙空技术网

耗时 18 个月,我们构建了一个真正可扩展的无服务器 SQL 数据库

InfoQ 4491

前言:

此时同学们对“数据库服务器可以虚拟化吗”都比较重视,各位老铁们都想要学习一些“数据库服务器可以虚拟化吗”的相关资讯。那么小编同时在网络上收集了一些关于“数据库服务器可以虚拟化吗””的相关文章,希望兄弟们能喜欢,看官们快快来了解一下吧!

在经历了 18 个月的挑战之后,我们推出了第一个真正可扩展的无服务器 SQL 数据库。这东西现在可以免费使用!请继续阅读本文,了解 CockroachDB 无服务器是如何由内而外工作的,以及我们为什么可以免费提供这种服务——不是在某段时间内免费,而是永远免费。要实现这一目标,需要进行许多重大的、迷人的工程。你一定会喜欢这个故事的。

什么是 CockroachDB 无服务器

如果你之前创建了一个数据库,根据你的预期流量,你可能需要评估使用的服务器数量。加入预测值过低,你的数据库就在负载中倒下,从而导致停机。如果预测值过高,或者流量猛增,你就会浪费钱在这些空闲的服务器上。能否有更好的办法呢?

无服务器意味着你无需考虑服务器的问题。诚然,有些服务器正忙于处理应用程序的请求,但这是我们的问题,与你无关。通过这些努力,我们可以在幕后分配、配置和维护服务器。你不需要为服务器付费,而是为你的应用程序在数据库上的请求和数据所消耗的存储付费。

你只需要为实际使用的花费买单,而不必预先计算可能的花费。如果你用得多,我们将会自动分配更多的硬件以处理增加的负载。如果你用得少,那么你就可以少付点钱,甚至不用付钱。如果你能设定一个有保障的每月消费限额,那么你将永远不会对账单感到惊讶。如果你接近该限额,我们将提醒你,在你超出该限额时,甚至免费提供性能基线水平。

这使我想到最重要的部分。CockroachDB 无服务器是“永远免费”的,每月为请求和存储提供慷慨(且永不终止)的信用。只要点击几次鼠标或者调用一个 API,你就能在几秒钟内创建一个全功能的 CockroachDB 数据库。你将得到一个“永远在线”的数据库,它可以在数据中心故障时依然存在,并为你的数据保留多个加密副本,这样你就不会因为黑客或硬件故障而丢失数据。它能够自动且透明地扩展,以满足你的需求,无论大小,都无需修改你的应用程序。它支持在线架构迁移,兼容 Postgres,并允许你获得企业特性。

在你选择的任何应用环境中使用你喜欢的语言、SDK 或工具;使用 CockroachDB 无服务器并不意味着你必须使用无服务器计算服务,比如 AWS Lambda 或 Google Cloud Functions 等。

我们怎么能承担得起赠送这个东西呢?毫无疑问,我们希望你们中的一些人能够成功地开发出应用程序,“大干一场”,成为付费用户。但是,除了这些之外,我们还创建了一种创新的无服务器架构,允许我们在一个单一的底层物理 CockroachDB 数据库集群上安全地托管成千上万的虚拟化 CockroachDB 数据库集群。这就是说,一个只有几千字节存储空间和少量请求的小型数据库,其运行成本几乎为零,因为它只是在一小块物理硬件上运行。下面我会详细地解释一下它是如何工作的,但是这里有一张图让你思考:

单租户架构

在此之前,单个物理 CockroachDB 集群是供单个用户或组织专门使用的。这就是所谓的单租户。在一些 CockroachDB 之前的版本中,我们已经添加了多租户支持,这使得一个物理 CockroachDB 集群可以由多个用户或组织(称为“租户”)共享。每一个租户都有自己的虚拟化 CockroachDB 集群,该集群托管在物理 CockroachDB 集群上,但是可以安全地隔离其他租户集群。你也许对虚拟机的工作方式很熟悉吧?它有点类似,仅用于数据库集群。

要想有意义地解释多租户的工作方式,我需要回顾一下单租户架构。首先,一个单租户的 CockroachDB 集群由任意数量的节点组成。每一个节点都用于数据存储和计算,它们通常托管在自己的机器上。CockroachDB 在单个节点上具有分层架构。最高层是 SQL 层,用于解析、优化和执行 SQL 语句。这是通过将高级 SQL 语句巧妙地转换为简单的读写请求,然后发送给底层的键值层。

键值层维护一个事务性的、分布式的、复制的键值存储。这很拗口,所以让我把它分解一下。每一个键都是一个唯一的字符串,映射为任意值,就像在字典里一样。键值将这些键值对以排序的方式存储,以便快速查找。键值对也被分组为范围。每个范围包含全部键值对中连续的、不重叠的部分,按键排序。范围分布在可用的节点上,为了高可用性,至少要复制三次。在全有或全无事务中,可以添加、删除和更新键值对。下面有一个简化的示例,展示了如何将更高级的 SQL 语句转换为简单的 KV GET 调用:

在单租户 CockroachDB 中,每个节点上的 SQL 层与键值层都在在同一个进程中。虽然 SQL 层总是调用在同一个节点上运行的键值实例,但是键值通常会“扇出”其他节点上运行的其他键值实例额外调用。这是因为 SQL 所需要的数据往往位于分散在集群中各节点的范围内。

多租户架构

怎样扩展这种单租户架构以支持多个租户?每个租户都会觉得自己拥有自己专用的 CockroachDB 集群,并在性能和安全方面与其他租户隔离。但是如果我们试图在租户之间共享 SQL 层,这就很难实现了。某个租户失控的 SQL 查询可以轻易地影响同一进程中其他租户的性能。另外,共享同一个进程会带来很多难以可靠缓解的跨租户安全威胁。

一种可能的解决方法是为每一个租户提供一套自己的隔离进程,同时运行 SQL 和键值层。但是,这造成另一个问题:我们不能在租户之间共享键值存储。这样就消除了对哦住户架构的一个主要优点:有效地将许多小型租户的数据打包到共享的存储层中。

考虑过这个问题后,我们认识到,通过分离一些组件并共享其他组件,可以很好的解决这个难题。由于 SQL 层难以共享,因此我们决定在每个租户的进程中将它的键值层的事务组件和分发组件隔离开来。与此同时,在所有租户共享的存储节点上继续运行键值的复制组件和存储组件。这样,我们可以得到“两全其美”的效果:对于每个租户来说,SQL 进程的安全性和隔离性,以及共享存储节点的效率。下面是最新的图表,展示了两个隔离的每租户 SQL 节点与共享存储层之间的交互情况:

存储节点不再运行租户的 SQL 查询,但它们仍然利用复杂的基础设施,为单租户 CockroachDB 提供支持。节点故障的检测和修复不会影响到数据的可用性。租赁者,为每个范围提供读取服务,协调写入,根据活动情况进行移动。繁忙范围会被自动分割;安静范围会被合并。基于负载的不同,范围在各节点之间重新平衡。存储层将热范围缓存在内存中,并将冷范围推送到磁盘。跨可用性区域的三向复制确保你的数据存储得到安全性和高可用性。

在看到这种架构后,你可能会想知道共享存储节点的安全性问题。为了保护租户数据,我们花费了大量时间设计和实施强有力的安全措施。每一个租户都会键值密钥空间的一个隔离的、受保护的部分。实现方法是在 SQL 层生成的每个密钥之前加上租户的唯一标识符。而不是生成一个像 /<table-id>/<index-id>/<key> 的键,SQL 将生成一个像 /<tenant-id>/<table-id>/<index-id>/<key> 的键。这意味着由不同租户生成的键值对在各自的范围内进行隔离。另外,存储节点还将认证所有来自 SQL 节点的通信,并每个租户只能访问以他们自己的租户标识符为前缀的密钥。

除了安全性外,我们还关心如何确保跨租户的基本服务质量。如果多个租户对同一个存储节点执行键值调用,会发生什么情况?为了确保单个租户无法垄断存储节点上的资源,我们测量来自每个租户的读取和写入请求的数量和大小,如果超出某个阈值,则限制其活动。不像 SQL 语句,键值调用是相对简单的操作,比如对键值对的 GET、PUT 和 DELETE,可以在一个共享进程中有效地调节。

无服务器架构

等等……上一节不是说无服务器架构吗?嗯,是的,也不是。如上所述,我们对核心数据库架构进行了重大升级,以支持多租户。但这只是故事的一半。要让无服务器成为可能,我们还需要对如何部署和操作多租户 CockroachDB 集群方面进行重大改进。

我们的托管云服务使用 Kubernetes(K8s)来操作无服务器集群,包括共享存储节点和每租户的 SQL 节点。每一个节点都运行在自己的 K8s pod 中,它就是一个带有虚拟化网络的 Docker 容器,并且 CPU 和内存容量有限。在深入研究之后,你将找到一个 Linux cgroup,可以可靠地限制进程的 CPU 和内存消耗。这样,我们就可以根据每个租户方便地测量和限制 SQL 资源消耗。这也可以最大限度地减少同一机器上调度的 pod 之间的干扰,即使其他租户正在运行繁重的工作负载,也可以为每一个租户提供高质量的体验。

下面是一个典型设置的高级(简化)表示:

那些“代理 pod”在 K8s 集群中的作用是什么?结果发现它们非常有用:

它们可以让很多租户共享同一个 IP 地址。在出现新的连接时,代理“嗅探”传入的 Postgres 连接数据包,在 PG 连接选项中查找租户标识符。这一步已经知道应该将这个连接路由到哪个 SQL pod。

它们在租户的可用 SQL pod 上平衡负载。向 CPU 负载最小的 pod 发送新的连接。

它们检测并应对涉嫌滥用服务的情况。这是我们为保护你的数据而采取的安全措施之一。

当云负载均衡器将一个新的连接路由到其中某个代理 pod 之后,代理 pod 又会将这个连接转发到连接租户所有的 SQL pod。每一个 SQL pod 只能提供一个租户专用,而同一个租户可以拥有多个 SQL pod。除非 SQL pod 属于同一个租户,否则网络安全规则禁止彼此对话。最终,SQL pod 通过键值层通信来访问共享存储 pod 管理的数据,每个 pod 都将数据存储在云提供商的块存储系统中,例如 AWS EBS 或 GCP PD。

除创建速度外,无服务器 SQL pod 在成本方面也有巨大优势。它们可以“挤”在一个虚拟机上,共享同一个操作系统,以及可用的 CPU 和内存。这样做可以显著地降低运行工作负载很小的“长尾”租户的成本,因为每个租户都能使用一小块硬件。相对于一个专用虚拟机来说,它通常需要至少保留一个 vCPU 和 1GB 的内存。

扩展

由于租户拥有的数据量越来越大,并且越来越频繁地访问这些数据,租户的数据将被分割为越来越多的键值范围,这些键值范围将被分散到更多的共享存储 pod。CockroachDB 已经很好地支持了这种数据扩展,并且在多租户集群中的操作方式与在单租户集群中的操作方式基本相同。由于篇幅有限,我将不再赘述。

同样,随着针对租户数据运行的 SQL 查询和事务数量的增加,分配给该租户的计算资源必须以一定比例增长。某个租户的工作负载可能需要几十个甚至几百个 vCPU 来执行,而另一个租户的工作负载可能只需要一个 vCPU 的部分时间。实际上,我们希望大多数租户都不需要任何 CPU。这是因为大部分尝试 CockroachDB Serverless 的开发者只是在购买之前仔细权衡。他们会创建一个集群,也许会对它进行一些查询,然后放弃,很可能就是永远的放弃。即使是为他们的集群保持一小部分 vCPU 的空闲,如果加上所有不活动的集群,则会造成巨大的资源浪费。甚至对于经常使用集群的租户,SQL 流量负载也不稳定;它可能每天、每小时、甚至每秒波动很大。

CockroachDB 无服务器是如何应对大量资源需求变化?基于每个租户的每秒流量负载动态分配 SQL pod 的适当数量。最佳情况下,可立即分配新容量,而最坏的情况则可在数秒内分配。同时,还可以通过低延迟的方式平稳地处理租户流量高峰。类似地,当通信量下降时,SQL 处理能力可以重新分配到其他地方,从而拥有最小的未使用能力。当流量降到零时,一个非活动租户拥有的所有 SQL pod 将被终止,新的 SQL pod 在新的流量到达时将在几百毫秒内重新启动。这样,很少被使用的 CockroachDB 无服务器集群仍然能够提供生产级延迟,而且对于 CockroachDB Labs 和用户也不会带来任何成本。

多租户 CockroachDB 将 SQL 层与键值存储层分割开来,这样的响应式扩展才有可能。由于 SQL pod 是无状态的,因此可以任意创建和销毁,而不会影响租户数据的一致性和持久性。无需在 pod 之间进行复杂的协调,也无需对有状态存储 pod 进行仔细的调试并退出,以确保所有数据都是一致且可用的。与通常保持长时间运行的存储 pod 不同,SQL pod 是短暂的,可能在启动后几分钟内就被关闭了。

Autoscaler

现在,我们再进一步看看扩展的机制。在每个无服务器集群中,都有一个 Autoscaler 组件,负责确定分配给每个租户的 SQL pod 的理想数量,无论是一个、多个还是零。Autoscaler 监控集群中每个 SQL pod 的 CPU 负载,并根据两个指标来计算 SQL pod 的数量:

最近 5 分钟内的平均 CPU 使用率。最近 5 分钟内的 CPU 使用峰值。

在 SQL pod 中,平均 CPU 使用率决定分配给租户的“基线”数量。基线有意超额配置 SQL pod,这样每个 pod 都有空余的 CPU 可供即时突发。但是,如果最近的 CPU 使用峰值甚至超过了较高的超额配置阈值,那么 Autoscaler 可以通过增加 SQL pod 超出基线的数量来解决这个问题。这种算法结合了移动平均数的稳定性和瞬时最大值的响应能力。Autoscaler 避免过度频繁的扩展,但是仍然能够快速地检测和处理负载的巨大峰值。

当 Autoscaler 得出 SQL pod 的理想数量时,它将触发一个 K8s 调整过程,增加或删除 pod,以达到理想数量。下图显示了可能的结果:

如图所示,我们维护了一个“预热”的 pod 池,这些 pod 随时可以使用;它们只需要使用租户的标识符和安全证书 “盖戳”。这个过程仅需几分之一秒的时间,而 K8s 从头开始创建一个 pod 需要 20~30 秒。如果需要移除 pod,它们不会被突然终止,因为这也会导致对该 pod 的所有 SQL 连接的粗暴终止。相反,pod 会被放置到“耗尽”(draining)状态,这为它们提供了更好地放弃 SQL 连接的机会。一旦所有的连接都消失了,或者 10 分钟过去了,以先到者为准,一个耗尽的 pod 将终止。

当应用负载降至零时, Autoscaler 最终决定暂停租户,这意味着将删除其所有 SQL pod。如果租户不再拥有任何 SQL pod,它将不会消耗任何 CPU、 I/O 或带宽。惟一的成本就是它的数据存储空间,与其他资源相比,它是相对便宜的。这也是我们可以为大家提供免费数据库集群的原因之一。

但是,还有一个问题有待解决。在未为租户分配 SQL pod 的情况下,租户如何连接集群?要回答这个问题,请记住每个无服务器集群都有一组代理 pod 正在运行。每一个由外部客户机发起的 SQL 连接都被代理 pod 截获,然后转发到分配给租户的 SQL pod。但是,如果代理发现当前没有为租户分配任何 SQL pod,它将触发与 Autoscaler 所使用的相同的 K8s 调整过程。从预热的 SQL pod 池中提取出一个新的 pod,并盖上戳,现在可以用于连接。整个恢复过程只需要几分之一秒,而我们正积极地努力使这个时间更短。

结论

既然您了解了 CockroachDB 无服务器的工作原理,我鼓励你访问 并尝试一下。如有任何疑问,请加入我们的社区 Slack 频道,提出问题。我还想听听你关于 CockroachDB 没有服务器的经验和反馈。在接下来的几个月里,我们会努力改进它。

其他资源

免费的在线课程:无服务器数据库和 CockroachDB 无服务器简介(Introduction to Serverless Databases and CockroachDB Serverless)。本课程介绍了无服务器数据库背后的核心概念,并提供必要的工具来开始使用 CockroachDB 无服务器。

原文链接:

标签: #数据库服务器可以虚拟化吗 #数据库服务器实例