首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

黄东旭:我对数据库如何Serverless 化的一些思考

写这篇文章的起因是有一次和朋友聊到一个很有趣的话题:如果我要为 1 亿用户提供免费的数据库云服务,这个系统应该如何设计?为了回答这个问题,我在之前的两篇 Blog 中隐隐约约提到,我们在使用一些全新的视角和思路去看待和构建数据库服务,并将这些思考变成了实际的产品:tidb.cloud/serverless,很多朋友很想了解 TiDB Cloud Serverless (下面会简称为 TiDB Serverless)的更多细节,所以便有了这篇博客。

有些朋友可能还不太了解 TiDB,请允许我先快速介绍一下 TiDB 本身。对 TiDB 比较熟悉的朋友可以跳过这段,主要是一些关键的名词和定义:

  • TiDB:特指由 tidb (负责 SQL 解析和执行) / tikv (负责数据存储) / pd (负责元信息和调度) 等组件组成的分布式数据库系统。
  • TiDB Cloud:一个提供 TiDB 的云服务,在云上提供全托管的 TiDB。
  • TiDB Cloud Serverless:一个提供 TiDB 的云服务,云原生 / 多租户,拥有极低的使用成本与启动时间,使用 Pay-As-You-Go 的付费模式,并在小规模的场景上完全面向开发者免费。

2015 年初,我们启动了一个非常有野心的计划。受到 Google Spanner 和 F1 的启发(我更愿意称之为鼓舞),我们希望构建一个理想的分布式数据库:支持 SQL、使用 MySQL 协议和语法、支持 ACID 事务语义、不对一致性妥协;对业务透明且无上限的水平扩展能力,能够根据而业务流量和存储特征动态调整数据的物理分布达到极高的硬件利用率;高可用及故障自愈能力,降低人工运维的负担。

在这些目标的驱动下,从一开始我们就做出了如下的技术决定:

  • 高度模块化。例如:tidb-server 负责 SQL 的解析和优化,tikv / tiflash 负责存储(和一部分分布式计算),pd 负责元信息存储及发起调度指令。
  • 存储层采用一个支持 ACID 事务的 Share-Nothing 架构的 Key-Value Database (tikv)以支撑弹性扩展,其他模块尽量设计成无状态的模式。如:当基础的 TiDB bootstrap 完毕后,很多信息都可以存储在 TiDB 自己的系统表中,如 SQL 优化要使用的统计信息等。尽量避免局部状态(例如不单独区分本地索引和全局索引)以降低故障恢复逻辑的复杂度。

可以看得出,这个设计就是奔着极强的扩展性和超大规模的场景去的,早期也确实如此。最初,TiDB 的想法来自于替换一些超大规模的 MySQL 分库分表方案,这个策略很成功,在无数的客户那已经得到验证,甚至还替换了一些以规模见长的 NoSQL 的系统,例如 Pinterest 在他们的 Blog 中提到他们将他们的 HBase 成功替换成了 TiDB(https://medium.com/pinterest-engineering/online-data-migration-from-hbase-to-tidb-with-zero-downtime-43f0fb474b84)

回顾当年做的一些决定,TiDB 是很正确的:

  • 大的模块上清晰的分离设计。计算、存储、调度、元信息管理都在不同的服务中,这样的好处是把复杂性限制在模块内部和无状态的组件(例如 TiDB 的 SQL 层)中,很容易水平扩展以及负载均衡。
  • 尽管用户看到的是一个关系型数据库,但是 TiDB 的存储层使用简单的 KV 作为抽象 + 极其细粒度的调度单元(region,96MB 的连续 Key-Value Pairs)+ 灵活的数据移动原语。好处是能够以很细的粒度移动数据,这让业务层理论上可以以任意的方式摆放任意数据,这是后来的 Placement Rules 等功能实现的基础。

但是随着时间的推移,我们渐渐发现了两个趋势:

  • OLTP 业务可以很好地支持横向水平扩展后,对于一个支持 SQL 的数据库来说,用户在上面进行简单的实时 OLAP 请求是很自然的选择,这个观察催生了 TiFlash。
  • 并不是所有的业务一开始都有如此巨大的数据需要分布式的存储,但是增长的预期是普遍存在的,所以更重要的问题是:当数据量和业务量真的增长到需要分布式数据库的容量时,开发体验是否还能保持一致(例如分库分表就是一个破坏开发体验的行为),而在刚开始数据量较小的时候又能以一个很低的成本起步。

上面的需求,在传统的 Shared-nothing 架构下,会有以下几点限制。

对于第一个趋势来说,OLTP 和 OLAP 确实在融合,但是在传统的 Shared-nothing 架构下其实是成本很高的,原因是对于 OLAP 应用来说,大多数时候 CPU 会成为瓶颈,CPU 是最昂贵且的资源。为了追求性能,我们只能增加更多的计算资源(也就是物理服务器),而 OLAP 业务通常又不是在线服务,所以这些 workload 其实并不要求 7x24 小时独占这些资源,但是为了运行这些 workload,我们不得不提前将这些资源准备好,即使 TiFlash 在存储层已经能很好的弹性伸缩(可以按需对特定的表 Ad-hoc 的创建 TiFlash 副本)但很难在非云的环境下对计算资源进行弹性伸缩。

对于第二个趋势,传统 shared-nothing 架构的分布式数据库通常假设所有物理节点的硬件配置对等,否则会加大调度的难度;另外为了追求极致的性能,通常会放弃多租户的设计,因为在云下大多数的数据库部署对于应用来说都是独占式的。而要降低数据库的使用成本不外乎两条路:

  • 多业务(租户)共享一个数据库集群
  • 冷热数据分离,使用更便宜的硬件存储冷数据。

另外就是业务开始上云,这点提的比较多,就不赘述了。

基于以上的假设,我们思考一个问题:如果今天重写 TiDB,应该有哪些新的假设、作出哪些选择,去覆盖更多应用场景和更高的稳定性。

假设:

  • 人人都爱低成本起步。
  • 按需计价,能够支持 Scale-to-Zero,不用的时候不收钱是很好的体验,而且当有海量用户时绝大多数用户是非活跃的,workload 的分布也满足二八定律。
  • 对于关系型数据库市场来说,OLTP 仍然是主流,OLAP 最重要的是弹性和低成本(其实并非性能)。另外一个容易被忽略的场景是轻量的 data transformation(一个好的例子是 dbt),频繁在不同的数据服务商之间移动数据并不是好体验。大量冷数据、少量热数据,但是常常没有办法很清楚地划分冷热边界(就像你不能突然阻止我在 3 年前的朋友圈上回复某个对话一样)。
  • 大数据量的在线服务场景是存在的(例如社交网络),但是一定会配合有区分度极高的二级索引使用(例如:user_id / account_id 等),在这个索引的覆盖下,一个查询扫描的数据量不会太大。这种场景下,内存缓存的命中率及其重要。CPU 很难拓展,存储很好扩展;CPU 很贵,存储很便宜。
  • 云基础设施在云上和云下都会普及(对象存储、容器平台等)。
  • “稳定 + 可预测的性能”,比不稳定的高性能更好。

选择:

  • Built-in 的多租户隔离能力:需要能够低成本支持粒度很细(小)的海量租户,毕竟只有共享资源才能将单位成本持续降低。
  • 存储层:利用不同的存储和计算服务,而不是对等的物理机器,尤其是利用好对象存储。
  • 仍然沿用分离的设计,但不再是以一个软件的形式交付,而是以一组微服务组成的平台服务进行交付。

更重要的选择是我在之前文章提到的:与其把数据库放到云上运维,更应该将云数据库作为一个完整的云服务设计。在这个指导思想下,我们开始了 TiDB Cloud Serverless,因为细节太多,我不可能在这篇文章内一一照顾到,思来想去挑了两个重点在这篇文章中介绍一下:存储引擎和多租户。

存储引擎设计

在传统的上下文中,我们提到存储引擎时一般都想到 LSM-Tree、B-Tree、Bitcask 什么的,另外在传统的语境中,这些存储引擎都是单机的,对于一个分布式数据库来说,需要在本地存储引擎之上再构建一层 Sharding 逻辑决定数据在不同物理节点上的分布。

但是在云上,尤其对于构建一个 Serverless 的存储服务,数据的物理分布不再重要,因为 S3 (对象存储,这里包含但也并不特指 AWS S3) 已经将扩展性和分布式可用性的问题解决得足够好(而且在开源社区也有高质量的对象存储实现,如 MinIO 等)。如果我们将在 S3 的基础之上构建的一个新的“存储引擎“看成整体的话,我们会发现整个分布式数据库的设计逻辑被大大简化了。

为什么强调存储引擎?因为计算层抽象得好的话是可以做到无状态的(参考 TiDB 的 SQL 层),无状态系统的弹性伸缩相对容易做到,真正的挑战在于存储层。从 AWS S3 在 2022 年的论文中我们可以看到,AWS 在解决 S3 的弹性和稳定性问题上花了极大的功夫(其实 S3 才是 AWS 真正的 Serverless 产品!)。

但是 S3 的问题在于小 I/O 的延迟,在 OLTP workload 的主要读写链路上不能直接穿透到 S3,所以对于写入而言,仍然需要写入本地磁盘,但是好在只要确保日志写入成功就好。实现一个分布式、低延迟、高可用的日志存储(append-only)比起实现一个完整的存储引擎简单太多了。所以日志的复制仍然是需要单独处理的,可以选择 Raft 或者 Multi-Paxos 之类的 RSM 算法,偷懒一点也可以直接依赖 EBS。

对于读请求来说,极低的延迟(<10ms) 基本上只有依靠内存缓存,所以优化的方向也只是做几级的缓存,或者是否需要用本地磁盘、是按照 page 还是按照 row 来做缓存的问题。另外在分布式环境下还有一个好处是,由于可以在逻辑上将数据分片,所以缓存也可以分散在多台机器上。

对于这个新的存储引擎,有以下的几个设计目标:

  • 支持读写分离

对于 OLTP 业务来说,通常读是远大于写的,这里的一个隐含假设是对于强一致读(read after write)的需求可能只占全部读请求的很小部分,大多数读场景可以容忍 100ms 左右 inconsistent gap,但是写请求对于硬件资源的消耗是更大的(不考虑 cache miss 的场景)。在单机的环境下,这其实是无解的,因为就那么一亩三分地,读 / 写 /compaction 都要消耗本地资源,所以能做的只能有平衡。

但是在云的环境下,完全可以将读写的节点分开:本地通过 Raft 协议复制到多个 Peer 上,同时在后台持续异步同步日志数据到 S3(远端的 compact 节点进行 compaction)。如果用户需要强一致读,那么可以在 Raft Leader 上读,如果没有强一致的需求,那么可以在其他的 Peer 节点上读(考虑到即使异步复制日志的 gap 也不会太长,毕竟是 Raft),即使在 Non-Leader Peer 上想实现强一致读,也很简单,只需要向 Leader 请求一下最新的 Log Index,然后在本地等待这个 Index 的 log 同步过来即可;新增的读节点,只需要考虑从 S3 上加载 Snapshot 然后缓存预热后即可对外提供服务。

另外,通过 S3 传输 snapshot 数据的额外好处是,在同一个 region 内部,跨 az 的流量是不计的。而且对于 HTAP 场景中的分析请求,因为数据都已经在 S3 上,且大多数分析的场景大约只需要 Read-committed 的隔离级别 + Stale Read 就能满足需求,因此只需要按照上面描述的读节点来处理即可,ETL 的反向数据回填也可以通过 S3 完成。

Before

After

  • 支持冷热分离

对于 OLTP 场景来说,我们观察到尽管总数据量可能很大,有时 OLTP 单个业务上百 TB 我们也经常见到,但是绝大多数场景下都有冷热之分,而且对于冷数据来说,只需要提供“可接受”的体验即可,例如读写冷数据的延迟容忍度通常会比较高。

但是就像开始提到的,我们多数时候并不能二元地划分冷热,但是我们又希望能通过冷热分离将冷数据使用更便宜的硬件资源存储,同时不牺牲热数据的访问体验。在 Serverless TiDB 中,很自然的,冷数据将只存储在 S3 上,因为分层的存储设计会很自然地将热数据保留在本地磁盘和内存中,冷数据在最下层也就是 S3 上。这个设计的坏处是牺牲了冷数据缓存预热时的延迟,因为要从 S3 上加载。但是就像上面说到的,在现实的场景中,这个延迟通常是可以被容忍的,想要提升这种情况的用户体验也很容易,再加入一级缓存即可。

  • 支持极快的弹性伸缩(Scale-out / in),并支持 Scale-to-Zero

在提到冷热分离的时候,有两个隐含的话题没有 cover:

>写请求热点如何处理?

>如何快速感知到热点?

要回答这两个问题,需要讨论:数据分片,请求路由以及数据调度,这三个话题也是支持弹性伸缩的关键。

我们先说数据分片。传统 Shared Nothing 架构的数据分片方式在逻辑上和物理上是一一对应的,例如 TiKV 的一个 region 对应的就是某台 TiKV 节点上的 RocksDB 一段物理数据。另一个更好理解的例子是 MySQL 分库分表,每个分片就是一台具体的 MySQL。

但是在云上,如果有这样一个云原生的存储引擎,那我们其实已经不太需要关心数据的物理分布,但是逻辑分片仍然是重要的,因为涉及到计算资源的分配(以及缓存)。过去 Shared-nothing 架构的分布式数据库的问题是:如果要调整分片就会涉及到数据移动和搬迁,例如添加 / 删除存储节点后的 Rebalance。这个过程是最容易出问题的,因为只要涉及到物理数据的移动,保证一致性和高可用都将是复杂的工作,TiKV 的 Multi-Raft 花了无数的精力在这块。但是对于基于 S3 的存储引擎来说,数据在分片之间的移动其实只是一个元信息修改,所谓的物理的移动也只是在新的节点上的缓存预热问题。

有数据的逻辑分片就意味着需要有对应的请求路由层,需要根据规则将请求定位到具体某台机器上。目前我仍然认为就像 TiKV 一样,KV Range 是一个很好分片方式。像字典一样,当你拿到一个单词,通过字典序就能很精准的定位。在 TiDB Serverless 中我们仍然保留了 KV 的分片和逻辑路由,但是会在原本的 TiDB 之上增加一层 Proxy 用于保持不同租户的 Session ,我们姑且叫它  Gateway。

Gateway 是租户隔离及流量感知的重要一环(后边会提到),  Gateway 感知到某个租户的连接被创建后,会在一个缓存的计算资源池中(tidb-server 的 container pool)捞出一个 idle 状态的 tidb-server pod 来接管客户端的请求,然后当连接断开或者超时一段时间后自动归还给 pool。这个设计对于计算资源的快速伸缩是非常有效的,而且 tidb-server 的无状态设计,也让我们在给 TiDB Serverless 实现这部分能力的时候简单了很多,而且在  Gateway 感知到热点出现后可以很快的弹性扩容。计算的扩容很好理解,存储层的扩容也很简单,只需修改一下元信息 + 在新的节点上挂载远端存储并预热缓存即可。

  • 经济性

在云上,一切资源都是需要付费的,而且不同的服务定价差别巨大,对比一下 S3 / RDS / DynamoDB 的定价即可看到很大的区别,而且相比服务费用和存储成本,大家很容易忽略流量的费用(有时候你看账单会发现其实比起存储成本,流量会是更大的开销)。节省成本有几个关键:

>利用 S3 进行跨 AZ 的数据移动和共享,节省跨 AZ 流量费用。

>利用 Spot Instances ,进行离线任务。离线任务定义:无状态(持久化在 S3 上)、可重入。典型的离线任务是 LSM-Tree 的 compaction 或者备份恢复什么的。

  • 能够重用原来 TiDB 的大部分代码

TiKV 对于存储引擎有着很好的抽象,其实主要修改的地方大概只有原来 RocksDB 的部分。关于 Raft 和分片的逻辑基本是能够复用的。至于 SQL 引擎和事务的部分,因为原本就和存储的耦合比较低,所以主要修改的部分就是添加租户相关的逻辑。这也是为什么我们能使用一个小团队,大概一年时间就能推出产品的原因。

多租户

想象一下,如果你需要支撑一个庞大规模的用户数据库,集群中有成千上万的用户,每个用户基本上只访问自己的数据,并且有明显的冷热数据区分。而且就像一开始提到的那样,可能有 90% 的用户是小型用户,但你无法确定这些用户何时会突然增长。你需要设计一个这样的系统,以应对这种情况。

最原始的方法很简单,就是直接将一批机器提供给一个用户,将另一批机器提供给另一个用户。这样的物理隔离方法非常简单,但缺点是资源分配不够灵活,并且有许多共享成本。例如,每个租户都有自己管控系统和日志存储等方面的成本。显然,这是最笨重的物理隔离方法。

第二种方法是在容器平台上进行部署,利用虚拟化,尽可能利用底层云平台的资源。这种方法可能比第一种方案稍好一些,至少可以更充分地利用底层硬件资源。然而,仍然存在前面提到的共享成本问题。

以上两种方案的核心思想是:隔离。其实包括所谓的在 SQL 引擎内部做虚拟机之类的方式本质上都是在强调隔离。但如果要实现用户数量越多、单位成本会越低的效果,光强调隔离是不够的,还需要共享:在共享的基础上进行调度,以实现隔离的效果。

TiDB Serverless 允许多个租户共享一个物理 Cluster 但是从不同用户的视角来看是相互隔离的。这是降低单位成本的关键所在,因为多个用户共享同一资源池。不过,这种基于共享的方案也存在一些问题,例如资源共享不足以满足大租户的需要、租户之间的数据可能会混淆、无法对每个租户的需求进行个性化的定制等。此外,如果一个租户需要定制化的功能,整个系统需要被更改以适应这个租户的需要,这会导致系统的可维护性下降。因此,需要一种更灵活且可定制化的多租户实现方式,以满足不同租户的不同需求。

我们看看在 TiDB Serverless 中是如何解决这些问题的。在介绍设计方案前,我们先看看一些重要的设计目标:

  • 不同租户之间可以不会相互影响 SLA,不同用户的数据互相不可见。
  • 不同租户之间可以灵活的共享 / 隔离硬件资源。
  • 系统故障的时候尽可能小的爆炸半径。

对于第一点,不同租户的数据如何隔离而且互相不可见,要回答这个问题需要从两个方面考虑:物理存储和元信息。

物理存储的隔离反而是容易的。在上面提到了 TiKV 内部是会对数据进行分片的,即使数据存储在 S3 上,我们也可以利用不同的 Key 的编码很好地区分不同租户的数据。TiDB 5.0 中也引入了一套名为 Placement Rules 的机制在语意层提供用户控制数据物理分布的操作介面(目前还没有在 TiDB 的云产品产品线中直接暴露给用户)(https://docs.pingcap.com/tidb/stable/placement-rules-in-sql)。TiDB 在设计之初为了性能考虑尽可能精简存储层对 key 的编码,并没有引入 namespace 的概念也没有对用户的数据进行额外的前缀编码,例如:不同用户的数据加上租户 id,为了实现租户的区分,这些工作现在都需要做,在存储层这部分并不算困难,更多要考虑的是兼容性和迁移方案。

传统的分布式数据库中,元信息存储是系统的一部分,例如在传统的 TiDB 中,PD 组件存储的元信息的主要部分是 TiKV 的 Key 和 Value 与具体的某个存储节点的对应关系。但是在一个 DBaaS 中,元信息远不止如此,更好的设计是将源信息存储单独抽离出来作为一个公共服务为多个集群服务,理由如下:

  • 元信息应该是多级的。就像上面提到除了最底层 Key-Value 和存储节点的对应关系外,还有 DB/Table Meta 与 Key-Value 的关系,租户信息和 DB/Table 的关系,租户和 Cluster Deployment 的关系等,这些信息对于调度是很有价值的。现在回头反思早期设计 的 TiDB,其中一个重要的 lesson learned 是元信息应该设计多一些逻辑分层,而不只是简单的 KV Range 的映射(这个在 Spanner 的设计中做的很好)。
  • 元信息服务应该是可扩展的。当元信息服务化后,意味着它可能需要支撑成千上万个集群的信息,这意味着元信息的数据量可能会很大,如果去看 Google Colossus 的元信息事实上是存储在一个 Big Table 上(https://cloud.google.com/blog/products/storage-data-transfer/a-peek-behind-colossus-googles-file-system)的。
  • 元信息会被数据库内核之外的其他各种服务依赖。最典型的例子是计费和联邦查询服务或者数据共享等场景。

关于实现租户之间的不可见性,还有另一个重要的模块,就是上面提到的 Gateway。如果没有这个模块,要共享一个底层的 TiDB Cluster 是不现实的。这很好理解,就像你不能拥有多个 root 账号一样。因为 TiDB 使用的是 MySQL 协议,为了实现多租户,总是要在一个地方识别租户的名字,但是 MySQL 的网络协议并没有为多租户系统设计。

为此,我们选择了一个“取巧”的方案:既然要识别租户信息,那就应该在 Session 建立之初识别,只需要在鉴权的时候将租户 id 传进来就好,之后 session 建立起来,我们自然知道这个链接属于哪个租户,至于如何兼容标准的 MySQL 协议?session variable?No,直接在用户名前加个前缀就行了,这就是为什么在 TiDB Serverless 的用户名前面有一些奇怪前缀的原因:

在连接建立之初就知道了租户 id 后,所有的逻辑隔离都可以通过这个 id 实现。

Gateway 会干比起普通 Proxy 更多的事情,事实上在 TiDB Serverless 中,我们直接将原本 TiDB 代码中的 Session 管理模块的代码单独抽出来,用于实现 Gateway。这个设计带来的好处很多:

  • 区分租户,感知连接的来源(地理 region),这个很明显就不用说了。
  • 流量感知 / 控制。Flow control 这类事情做在越上层越好,存储层都已经感受到压力山大的时候通常留给流控的空间就不大了。由于 Gateway 是一切流量的出入口,而且 TiDB Serverless 和 AWS Dynamo 在论文中提到的一样,引入了 RU (Request Unit,https://www.pingcap.com/tidb-cloud-serverless-pricing-details/) 的概念,在这里进行 RU 的控制是最精确的。
  • 无感升级 / 扩缩容。和第二点类似,当 Gateway 感知到突发流量或者长时间没有流量的时候,可以很方便告诉底层的 tidb-server(SQL processing) 资源池增减计算节点,或者对于不同类型的请求申请不同规格的计算节点。这点也是支持 Scale-to-Zero 的关键。
  • 计费。结合第二、第三点以及前面提到的存储层的自动冷热分离设计,Gateway 上针对具体租户的流量信息是:准确 + 底层可以根据实际流量动态调整底层的计算资源 + 通常成本很低的(S3)冷存储,这几点让我们都能够实现 Pay-as-you-go 的付费模式。即使 Gateway 本身是常驻的,但这部分成本均摊到海量的用户上是可以接受的。
  • 用户无感的升级。传统方案中如果底层数据库需要更新,必然会中断用户的连接,这对于有些保持数据库长链接的应用是不友好的(很多开发者基本上都不太会考虑断开重连的情况🤷)。而 Gateway 模块是比较轻量的也很少升级,于是我们可以在这层保持用户的连接,底层就可以做到无缝地更新进程。在用户侧观察到的显现无非就是某几个请求的延迟稍稍升高,之后就恢复正常了。
  • 极快的新集群启动时间。新集群的创建只是添加一些元信息 + 从 tidb-server 的资源池里捞一个 pod 出来的代价 :)

Gateway 作为常驻模块,本身也是无状态的。增加的延迟和与带来的好处相比,我们认为是可以接受的。

我们通过 Gateway 和元信息的修改实现了多租户在一个物理集群上的逻辑层的隔离,但是如何避免 Noisy Neiberhood 的问题?在上面提到我们引入了 RU 的概念,这里就不得不提一下我们在 TiDB 7.0 中引入的名为 Resource Control 的新框架(https://docs.pingcap.com/tidb/dev/tidb-resource-control)。和 Dynamo 类似,TiDB 的 Resource Control 也是使用了 Token bucket 的算法(https://en.wikipedia.org/wiki/Token_bucket)。将不同类型的物理资源和 RU 对应起来,然后通过 Token bucket 进行全局的资源控制:

比起硬性的物理隔离,这样实现资源隔离方式的好处是很明显的:

  • 灵活。因为这套机制对用户的访问层是没有侵入的,也就是和 TiDB 原本的 Data Placement 机制是正交的:如果一个大客户需要完全隔离的环境,可以配合 Placement Rule 将用户的数据移动到一批额外的硬件资源上,就完成了硬隔离;反向的话,也很容易实现。
  • 支持 Burst 和抢占式的资源共享方式。在总资源有限的情况下,应对重要业务突发流量的很重要的应对方式是 Burst 和抢占,也就是将低优先级的资源(或者预留的资源)临时借给高优先级的应用,这点对于降低总成本的同时获得一个良好的用户体验(对于付费客户)很重要。
  • 资源预留以实现 Predictable 的性能。这点和上面类似,但是不太一样。在 Dynamo 2022 年的新论文中提到类似的 DBaaaS 更重要的是提供 Predictable 性能的概念,我非常认同,而且我认为 Predictable 的关键在于尽可能避免硬件资源过载,而避免硬件资源过载最有效的办法就是提前预留资源,因为所有资源都被这套 Resource Control 框架精确控制,所以预留资源也是很容易实现的。

其实还有更多好处也在 Dynamo 那篇论文里提到,这和我们实际的感受是一致的。

但是,基于共享大集群的多租户服务实现,会带来一个挑战:爆炸半径的控制。例如当某一个服务出现故障或者发现严重的 Bug,可能影响的范不是一个租户,而是一片。目前我们的解法是简单的 sharding,至少一个 region 故障不会影响到另一个 region。另外对于大租户,我们也提供传统的 dedicated cluster 服务,也算是在业务层对这个问题提供了解决方案。当然,这方面我们也仍然在持续的探索中,有些工作很快会有一些可以看到的效果,到时候再与大家分享。

还有很多很有趣的话题,例如上面提到整个系统都是通过 Kubernetes 串联在一起的,那么为这样一个复杂的 DBaaS 提供 Devops,如何池化云上资源和多 region 支持?另外设计思路的改变不仅影响了上面提到的内容,对于数据库的周边工具的设计也会有重大的影响,例如 CDC、备份恢复什么的。因为篇幅关系就不在这里写了,以后有机会再说。

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/gEogP4tDBz3LQv5ym4Ek
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券