把云数据库服务变成黑盒子:ServerlessDB for HTAP丨Hacking Camp 进行时

ServerlessDB for HTAP 的最终目标是要把云数据库服务变成黑盒子,让应用开发者只需要专注于业务如何转化成 SQL,并保证任意时刻,任意 SQL 云数据库服务都能在合理的时间返回正确的结果。我们的用户再也不用操心数据量、业务负载、数据库参数、AP 还是 TP SQL、高可用等这些和业务不相关的事情。

本次参与项目的成员全部来自于移动云数据库团队,主要负责在移动云上基于 TiDB 提供 ServerlessDB 服务。本次参与 ServerlessDB for HTAP 项目成员分工如下:

  • 薛港:负责整体架构设计,以及中间件核心模块开发.
  • 沈政:负责 serverless 模块的设计以及开发工作,实现计算存储 scale up / down, scale in/out
  • 时丕显:负责业务负载模块的设计以及开发工作;完成模块之间的集成测试
  • 金汭:负责所有模块 API 设计开发工作,提供所有组件的容器化方案;负责针对AP场景的serverless 模块开发

要基于 TiDB 实现 serverless 服务,需要解决一系列问题,重点列出几点:

  • 什么时候扩缩容,每次扩缩多少。
  • 如何保证扩容以后,每个节点的负载均衡,以及缩容过程中用户的零感知。
  • 如何区分 AP 和 TP 的 SQL 类型,最终保证运行时,相互干扰减少到最低。
  • 如何提供多个云服务实例。

针对上述问题,我们的解决思路如下:

  • 开发业务负载模块,评估当前提供服务的资源与当前业务负载是否匹配,建立业务负载模型,用于决策扩容还是缩容,扩缩多少。模型参考因子,包括 CPU、SQL 成本、QPS 等。
  • 为了解决扩容后业务负载快速分摊到所有的节点,以及缩容时业务负载从待缩容节点迁移走,因此我们开发了 TiDB 中间件。
  • 当前我们通过 SQL 成本区分 TP 和 AP 类型,底层资源提供多个计算池,负责不同 SQL 成本的 SQL 运行,例如一个 TP 计算池,1 到 N 个 AP 计算池。
  • 基于 TiDB Operator 实现多个实例的云服务管理,我们提供 K8s 本地盘管理,用于解决私有化部署无法提供云盘的问题。我们也通过开发 admission-webhook,保证删除 tidb pod 时,预先删除中间件注册表记录。

一、目前的进展

1、serverless 模块 90%

在 serverless 模块中,我们定义一个指标“算力”,整个过程是通过比较它的变化来决定 TiDB 扩容或缩容,1 算力近似等于 1 个 CPU 和 2 GB 内存的计算资源,算力的增加对应的 CPU 和内存是 1:2 的关系。

1) 针对 AP、TP 不同类型 TiDB 节点的扩缩容模块 90%

  • TP 类型节点的扩缩容

标注:TC 指 tidbclusters.pingcap.com 的 crd,多个 tc 对象管理不同规格切换,副本指实例数

proxy 模块给 serverless 模块发送基于当前 TP 类型节点的总成本计算出的算力值,同时 serverless 模块自身也会从 promethues 读取 CPU 使用量再计算成算力值,当几个节点的 CPU 平均使用量超过 50%,则认为需要翻倍扩容,我们遵循扩容自由缩容谨慎的原则。对于扩容,serverless 会比较两个算力值(proxy 和 promethues),取较大值作为目标值来进行扩容;缩容则较为钝感,只有当中间件发送算力值时带有缩容的标签,则对应算力值做缩容。

扩容算法:

为了计算出目标值需要的 TIDB 节点的规格以及数量,事先定好一个列表包含多个规格,例如 [0.5,1,2,4],该列表最大值和最大副本数可动态配置,目前配置的最大副本数为 8,那么该规格列表即可覆盖最大 48=32 算力(32C),每个规格对应的算力范围为 [0.5,1-8,2-16,4-32],0.5 为最小保护算力,即始终起一个 0.5 算力的 tidb 作为业务备用,其他算力范围则为 8 的倍数,如果当前算力值为 4(1C4 副本),需要扩到 8,目标值 8 刚好依然在 1 核的范围之内(1-8),则直接扩大副本数为 8 即可,若目标算力为 16,此时需注意我们起 TC 都是遵循先起小 TC 的原则,因为经过测试同样算力的小 TC 性能更高,所以从小到大选择规格,优先选择 2C 乘以 8 副本的方式起 TC,如果目标值是 9,通过计算需要起 2C 乘以 4 副本后,还剩一个算力,此时会计算剩余值是否大于该规格的 50%,如果大于等于则再起一个副本,如果小于则忽略,在这个例子中,剩余值 1 大于等于 1C,则再起一个副本,那么目标值是 9 会起 2C 乘以 5 副本。

缩容算法:

和扩容算法类似,也同样去计算确认规格区间和副本数,不同点是,如果目标算力小于等于当前规格最大算力的 25%,则重新由小到大挑选规格,因为同等算力下小规格的 TiDB 性能更好,例如当前算力是 16(2C*8 副本),目标算力是 3,3 小于 16 的 25%,所以在规格表 [0.5,1,2,4] 中,按照从小到大的挑选原则,即缩小为 1C 乘以 3 副本,如果目标值是 9,9 大于 16 的 25%,则只需修改副本数,缩到 2C 乘以 5 副本。

  • AP 类型节点的扩缩容

目前 AP 不会跨规格扩缩容,只会起一个规格(4C16G)的 TC,proxy 模块会基于 AP 类型节点的总成本计算出算力值,根据该算力值去增删副本数。

此外还会建一个大规格(16C32G)的 TC 去处理成本高、耗时久的 SQL,即用即停,当收到代价较高的 SQL 请求后,proxy 模块通知 serverless 新增一个 Pod 去处理它,处理完成后再删除。

2)根据存储容量对 TiKV 节点进行扩容 100%

对于 TiKV,serverless 只会对其扩容,不会缩容,扩的原则是 tikv 本地存储实际使用量占tikv存储规格的 70%,tikv 横向增加一个实例,当横向扩容的 tikv 实例数达到环境变量配置的上限如 10 个 tikv 时,进行纵向扩容,因为我们组件含有本地盘且实现了存储扩容,直接修改 tikv 的存储 request 每次增加 20G,就可实现所有tikv实例存储自动增加 20G。

3)规则模块 100%

为了应对业务负载激增的特殊情况、保持服务的持续稳定,因此在基于负载进行动态扩缩容之外,我们还设置了规则系统,每个规则描述一个时间段的资源规格。在规则生效时间内,计算资源不会发生任何变化。在规则时间外,还是会基于负载实现扩缩容。目前规则时间最小粒度为小时,支持按天、周、月作为重复周期(重复周期不支持跨天设置,仅一次性周期支持跨天设置。)

2、proxy 模块 80%

基于 TiDB 改造,开发支持代理和计算的混合中间件。当业务负载低时,中间件节点退化成纯 TiDB 节点,承载所有 TP 类型的业务流量。随着业务负载变高,会扩容出其它 1-N 个 TP 类型的计算节点,业务负载会按照 CPU 比例分配业务流量到中间件以及其它计算节点。

如果业务负载流量达到一定的上限,中间件会升级成纯粹的代理节点,把业务流量打散到所有后台计算节点。 部分功能模块如下:

1) 中间件代理框架开发 80%

针对不同的 mysql cmd 以及 SQL 类型,保存运行时上下文,转发请求到后台计算节点,得到计算节点的计算结果,最终通过中间件返回结果给客户端,整个过程用户完全感受不到连接的是中间件

2) 针对不同类型 TiDB 节点分配实现负载均衡 90%

为了避免不同类型的 SQL 互相影响,因此我们将后台的 TiDB 节点分为三种类型:

  • TP 类型

其中 proxy 节点本身也属于 TP 类型,当 TP 类型只有一个节点,即 proxy 节点时,则 proxy 节点作为纯计算的节点,所有的 TP 请求都在 proxy 节点进行处理;当 TP 类型有多个节点并且 proxy 节点的负载不高时,则 proxy 节点作为一个普通的 TP 类型计算节点;当 proxy 节点负载非常高时,则 proxy 节点只负责转发请求,不再作为计算节点。而所有 TP 的请求采用平滑的加权轮询算法均匀的分发到不同的计算节点,并且计算节点的权重值是基于该节点的规格。

  • AP 类型

所有 AP 的请求采用平滑的加权轮询算法均匀的分发到不同的计算节点,并且计算节点的权重值是基于该节点的规格,目前 AP 类型的计算节点规格只有一种类型。

  • 超大规格类型(用于处理单条成本很大的 SQL)

当收到一条很复杂的 SQL 请求时,proxy 模块会通知 serverless 模块起一个超大规格的 pod,当 pod 可以提供服务后,proxy 模块会将该 SQL 转发到新起的大规格 pod 上,等 SQL 执行完成后,proxy 模块通知 serverless 模块删除该 pod。

3、本地盘模块 100%

1) 实现 K8s 本地盘的动态管理 100%

本地盘主要基于 K8s 开放的标准 CSI 存储接口以及 LVM 技术实现,主要实现了本地存储容量管理以及 PV 动态创建、删除、pv 扩缩容以及本地存储剩余容量参与容器调度。

  • 实现步骤

主要实现 CSI Identity、CSI Node、CSI control 的接口功能,在 pod 未调度到具体节点前,pv 与 pvc 的 Bound 不关联具体存储介质,待 pod 调度具体节点时,然后使用 lvm 相关命令 lvcreate 在 vg 卷组中动态创建逻辑卷,将 pv 关联到逻辑卷,同时将 pv 上打节点标签信息,以便 pv 在后续使用过程中能找到具体节点。另外,pod 的调度我们开发lvm-scheduler 调度器组件容器,该组件容器同时兼容 k8s 原生的 cpu、memory 调度并加入剩余本地存储容量作为调度因子,每个节点上管理本地存储的 CSI-hostpath-plugin 组件定期更新本地剩余存储容量,lvm-scheduler 调度器选择 cpu、memory、存储空间最优的节点作为调度节点。pv 的扩容,k8s 监控 PVC 的容量变化后,CSI Node 组件容器通过 lvextend 实现对逻辑卷的动态扩容。pv 的删除,最终触发 CSI Node 组件容器进行 lvremove 进行逻辑卷删除。

  • 实际效果

本地盘部署完成后,与使用云盘块存储一样方便,我们只需要在创建 PVC 时关联我们提供的 storageclass,应用容器使用本地存储时,本地盘容器会自动分配相应容量给应用容器。

4、admission-webhook 模块 100%

1) 实现节点缩容时对用户无感知 100%

主要通过 admission-webhook 模块实现在缩容时对用户无感知的目标。在进行缩容时,借助 advanced-statefulset,决定哪些节点将被删除,然后 admission-webhook 可以拦截删除 tidb pod 的请求。首先,admission-webhook 模块向 proxy 模块发送删除该 pod 的请求,proxy 模块将停止转发请求到该 pod 上,并确保以执行的 sql 顺利结束,当该节点没有待执行的 SQL 时,proxy 模块将通知 admission-webhook 模块释放该删除请求,最终admission-webhook 允许删除该 pod,实现用户无感知的缩容。

二、issue列表

1、https://github.com/tidb-incubator/Serverlessdb-for-HTAP/issues/2:judge whether a SQL is AP or TP

2、https://github.com/tidb-incubator/Serverlessdb-for-HTAP/issues/3:How to evaluate whether to scale-out or scale-in, and what is the number of nodes to scale-out or scale-in

3、https://github.com/tidb-incubator/Serverlessdb-for-HTAP/issues/4:Performance problems when the cluster is running on a small hashrate

4、https://github.com/tidb-incubator/Serverlessdb-for-HTAP/issues/5:TiDB nodes assignment algorithm when receiving a request

5、https://github.com/tidb-incubator/Serverlessdb-for-HTAP/issues/6:For AP process, elegant start and stop auto-scaling

6、https://github.com/tidb-incubator/Serverlessdb-for-HTAP/issues/7:Users will not be affected at all when auto-scaling

三、后续规划

  • 优化减少因为引入中间件对性能造成的影响
  • 业务负载模块的精细化,两个方向,一是运行前各类资源使用评估(例如每个算子评估资源使用量) 二是实际运行过程中的资源评估(CPU,内存,网络)
  • 如何加速扩容时间,当前我们基于容器方案,启动时间 10-20s,如果优化新增节点的时间,例如优化到 1s。节点启动时间对 serverless 至关重要,从发现业务负载到完成节点启动,时间越长,对服务影响越大
  • 如何解决小算力场景下,业务负载暴增的问题
1赞

是否有机会参与该项目呢

可以在项目的 issue 列表中看有没有感兴趣想参与的哦~
https://github.com/tidb-incubator/Serverlessdb-for-HTAP/issues