3

多租户实现路径的探索

 2 years ago
source link: https://www.zenlife.tk/tidb-multi-tenant.md
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

多租户实现路径的探索

2022-08-03

这一篇探讨一下 tidb 上云的事情。本篇有可能就是<云原生数据库架构系列>的最后一篇了。

当一个项目很小,或者从头开始时,面向云原生设计是一个比较容易的事情。当一个项目太大了之后,改造就很困难,系统太复杂了,走不动。

如果架构并没有适配云的特点,那叫可以在云上跑,把集群部署到云上面,不叫云原生。早期的 tidb 其实处于这种状态。

然后就是托管,加上一部分基础设施之后,用户可以很容易在云上起一套集群,也能很方便地通过 web 控制台加节点,扩容缩容,这一层是云运维,但是架构仍然是不是云原生。

到这一步,数据库内核架构,跟云是一个割裂的状态,云把被托管的集群当一个黑盒在处理,而数据库也仍然当自己运行在物理机器上。仅仅利用到的是云的实例的弹性做集群的扩缩容。无非是把 DBA 的劳动量减轻一点,让工具更友好一点。

如果架构没有适应云的调整,其实上云的成本是很高的。用户会表示:嗯,东西还挺好的,就是太贵了。云服务提供商反正是卖机器,怎么都是赚钱的。而做云数据库服务呢,价格高了,没用户来。价格低了,自己就赚不到钱甚至要亏本了。所以成本其实是核心竞争力之一。

从这个角度,用户要的并不管背后架构是否先进,他们需要性价比。搭建一个基础的集群,tidb 至少一个节点,pd 1-3 个节点,tikv 3个节点,还有日志/监控/工具这些,很容易就 7~8 个实例起步。又要求磁盘性能,还有 CPU 和内存也不能太低,用户试用的门槛就太高了。

所以从节约成本的角度,有一条路线就是纵向缩容:从 8c 16G 实例起步,变到 4c 8G,甚至到 2c,这样成本可以降下去。这一块改动对产品内核还是带来不少挑战的,因为机器资源限制,很多不那么严重的问题被放大得更严重,比如说内存太少了使用中很容易就 OOM,比如 CPU 或者磁盘太差之后有些瓶颈点就变得突显。

这条路线是缩成本的一条路,同时,真正上生产的,跟用户试用的,可能是两套不一样的环境。所以为了吸引用户来试用产品,我们做了 development tier 这套的配置,相对成本低,适合试用。

多租户路线需要改动的部分很多,个人或者单个团队都很难完成这个规模的改动。 我记得有一次的 hackathon 好像有人做的是这个。从 RPC 层面去实现多租户。底层 tikv 共享,上层 RPC 额外传入一个租户 ID 的参数,tikv 靠租户 ID 去划分不同的 key range 使用。只要在 tikv 那层提供 keyspace 的能力,然后在 RPC 层提供租户 ID,就可以让多个租户跑在同一套 tikv 集群上面,从而摊平成本。从网上也找到了这样一篇文章

keyspace 比暴露直接 key 编码方案,其实是好一点的。虽然从实现角度看,都差不多一回事。keyspace 也要把 key 划分成不同的编码区域,给不同调用者调用。但是有一个重要的概念是:暴露接口而不是暴露实现细节。假设 tidb 直接约定租户的 key 怎么编码,比如说 /k_tenant_id/t_tid_i_xx,这就是在暴露实现细节。暴露实现细节的情况下,会有非常多的地方依赖于这个编码约定,只要一改动,可能周边工具就坏掉了,或者调度器坏了,或者可能哪里就触发 bug 了。这也是我们现在难处理的原因,因为之前各个组件都是暴露 key 编码约定的,一改就有很多地方要改,又容易漏。

如果 tidb 层不需要知道租户ID 这层的概念,比如说只有 RPC 层需要知道这个,那么代码改动的侵入就比较小。如果在 tidb 层需要知道租户 ID 的概念,那么改动的侵入就比较大。比如说表的编码,假设要从以前的 /t_tid_ 这样的前缀,就成 /k_tenant_id/t_tid_ 这样的前缀,涉及的修改量就巨大了。 有一种解决方式是,改造 tid 的含义,比如从字段里面拿出 16 位来当 tenant id,剩下的当原来的 table id,改动小一点儿,但是仍然是有侵入。而如果 RPC 层能完全屏蔽 tenant ID,则会更优雅:虽然 RPC 传了 tenant ID 之后,会被编码到某个 key range,但是调用者并不知道这是 keyspace 的划分,它看到的还是去掉 k_tenant_id 之后的那一层 key range,即它认为它自己是完整的拥有一个 key range 的。

实现多租户有两大块内容,其中一块内容是划分资源给不同的租户使用,keyspace 划分就属于这一块。另外一大块内容是,多租户间的资源隔离,即共享模型下如何保证单个租户的服务质量。

先继续看资源的划分使用,除了 key range 之外,以前的每个 tidb 都会有各自的其实资源,比如元信息,ddl,gcworker,analyze 等等。

集群的元信息有一部分是在 tikv 中存储的,那么随着 keyspace 和带租户ID 的 RPC,这一块就解了。有一部分依赖 pd,如果 pd 也是整体存储层共一套,那么这一部分依赖需要挪走。

ddl gcworker analyze 这些,在云架构下都应该做成服务化,它们都是后台任务,应该做成随用随起的。比如整集群共一个 DLL 服务,当租户需要做 DDL 的时候,向 DDL 发送请求,然后 DDL 服务动态拉起 worker 都处理任务,完成之后又可以释放掉这部分的资源。 gcworker 和 analyze 也是同理。

每一个点说起来都看似简单,但是落地起来事情就很多。现在是一个臃肿的复杂大系统,只能脑洞一些想法,负重前行吧!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK