弹性防抖动能力

最近更新时间:2024-09-23 16:04:51

我的收藏
Serverless 服务具备弹性防抖动能力,可帮助用户更好地适应不同的工作负载和性能需求,本文为您介绍 TDSQL-C MySQL 版实例形态为 Serverless 的集群的弹性防抖动能力。

背景

在 TDSQL-C MySQL 版的 InnoDB 存储引擎中,Buffer Pool 是一个至关重要的内存区域,用于缓存数据和索引。当查询数据时,首先会在 Buffer Pool 中查找,如果数据在 Buffer Pool 中(即命中了缓存),那么 InnoDB 可以立即返回结果,这就避免了磁盘 I/O 的开销。因此,对于提高数据库性能来说,正确配置 Buffer Pool 的大小尤为重要。Serverless 架构将购买的实例规格解耦,可以根据用户数据库实际的负载,自动启停和自动扩缩容,将计算资源做到极致弹性。其中计算资源主要是 CCU(CPU + 内存),CPU 可以由 cgroup 或者 docker 等技术限制,而内存分配给数据库进程,大部分由 Buffer Pool 模块使用,目的是缓存用户数据,Buffer Pool 内存的分配与释放过程涉及用户数据的分布、搬迁,还有内核中全局资源的互斥等。正确配置 Buffer Pool 的大小,能使 TDSQL-C MySQL 版 Serverless 服务提供更稳定的弹性服务能力。
MySQL 官方支持动态配置 Buffer Pool 的大小,使用方式是通过直接调整参数 innodb_buffer_pool_size 的大小实现,任务将在后台完成,如果在任务完成之前,再次改变参数,那么将被忽略。其扩容逻辑相对简单,而缩容逻辑相对复杂,也是容易出现瓶颈的地方,比如:IO 瓶颈、free/lru list mutex 瓶颈、全局锁瓶颈等。因此 TDSQL-C MySQL 版 Serverless 服务在内核上做了一系列优化,使得数据库能够“弹”得更稳。

瓶颈分析及优化方案

IO 瓶颈

内核团队在用官方 MySQL 8.0 测试过程中,发现缩容的主要瓶颈在于 flush lru list,因为大部分场景下,第一遍扫描 free list 都是不能满足回收要求的,而且根据需要回收的 block 数量来确认 scan depth,可能是一个比较大的值。buf_flush_do_batch 需要刷脏,持久化 page,在此过程中也会频繁的获取和释放 lru mutex,和用户线程竞争,产生毛刺。其中持久化 page 涉及 IO 操作,是主要瓶颈。这个问题在 TDSQL-C MySQL 版的架构上可以完全规避,因为分布式存储上的 page 都是由 redo log 在存储层 apply 异步生成的,计算节点不需要刷脏,需要淘汰的 page 可以直接丢弃。产品架构如下图所示。


free/lru list mutex 瓶颈

在缩容的主要流程中,每次循环都会遍历 free list 和 lru list,遍历的过程中都会持有相应的 mutex,此时无论是读操作还是写操作的用户线程是无法访问的,都可能需要获取 free/lru list mutex。Buffer Pool 中所有的 block 都维护在这两个链表上面,因此遍历的过程具有 O(N)(N 表示 block 数量)的时间复杂度,这个值可能非常大,持续持有 mutex 时间较长,导致用户出现毛刺。

优化方案

优化策略为按地址遍历需要被回收的 chunk 中的 block。这样遍历的 block 数量和缩容的大小相关,不依赖整个 Buffer Pool 大小。并且将加锁区间由整个 lru 链表变成单个 block。这将减少持锁范围和持锁时间。

全局锁瓶颈

无论是进行扩容还是缩容操作,都存在一段逻辑是需要获取 Buffer Pool 的全局锁,这期间 Buffer Pool 对用户几乎是不可用的。如果这一步骤执行时间过长,也会导致用户感受到性能的短暂下降,即“毛刺”。通过分析,我们发现这个过程中有三个主要环节较为耗时:
回收 chunks 内存并且 free blocks mutex。
分配 chunks 内存并且 init blocks。
Resize Hash。

优化方案

针对前两个环节,内核团队采取延迟释放 chunks 和提前预分配 chunks 的策略。这样主要的工作就可以在 buffer pool mutex 外。原本 O(N) 的复杂度,初始化 blocks 和 free blocks 导致 N 都是 block 数量,优化后 O(N) 的 N 是 chunks 数量,复杂度相对可控。针对 Resize Hash 环节,其实这个本质就是 Rehash 的问题,根本的解决方案是优化算法,例如 lock-free Hash 或者一致性 Hash 分配。Hash Table 是 Innodb 里的基础元素,如果改这个复杂度,风险会比较高,周期也较长。Hash 表如果太大会浪费空间,很多 cell 使用不上,如果太小就会有较多的 Hash 碰撞,影响性能。内核团队采取空间换时间的策略,让触发频率可配置,减少或者在一定扩缩容范围内,不触发 Resize Hash。

优化效果

测试使用 sysbench oltp_read_only,配置 long_query_time = 0.1,查看慢查询的数量。优化前后对比如下。


使用说明

针对以上瓶颈和优化,除了内核团队直接进行的优化以外,在 Resize Hash 环节,我们提供了相关参数设置,使得用户可以选择是否扩缩哈希表的大小,来防止毛刺的产生,毛刺产生的主要原因是扩缩哈希表引起的,因此不更新哈希表的大小可以防毛刺。关于参数的详细介绍如下。
参数名
是否重启
全局参数
默认
参数值范围
说明
innodb_ncdb_decrease_buffer_pool_hash_factor
2
0 | 2
调整缓冲池大小线程的频率会减小哈希表的大小。
值为2是关闭状态,表示当缓冲池减少超过1/2时,哈希表大小将被更新。
值为0是启用状态,表示哈希表大小不会更新。
说明:
当此参数为启用状态时,更改节点算力配置上限会重启实例。

参数支持的内核版本

内核版本 TXSQL 5.7 2.1.12及以上。
内核版本 TXSQL 8.0 3.1.14及以上。

参数设置方法

设置方法请参考 设置实例参数