KaiwuDB 分布式系统 Range Split & Merge 原理详解
背景介绍
Range(分区)是一种数据库管理组织数据的技术。在分布式系统中,我们可将数据按照一定规则分成多个 Range 进行组织,通过分裂(Split)和合并(Merge)来实现动态管理,用以优化查询性能,并帮助提升系统的扩展性、可用性及负载均衡。本期直播的主要内容即为:KaiwuDB 分布式系统里 Range 的分裂(Split) 与合并 (Merge)。*以下为部分内容节选,完整内容可点击视频查看完整版内容。
KaiwuDB Range 分裂(Split)
分裂情况介绍
splitQueue 负责 Range 的分裂,触发 Range 分裂的条件如下:
新建数据库或表。
Range 大小超过 range_max_bytes。
Range 的 QPS 过高,超过 kv.range_split.load_qps_threshold (默认值 250,可配置)。
修改 index 或 partition 的 Configure zone,使其从父级别中独立出来。在特殊情况下,会不经过 splitQueue,而直接调用 adminSplit 分裂。
在导入大量数据时,1 个 Range 自动分裂成多个 Range。
在导入数据时,为后续可能导入的数据预分裂出一个空白 Range。
手动分裂:alter table table_name split at values (key1,key2,…);其中 Values 表示主键值,若为联合主键可写多个,不能超过主键列数。
分裂算法流程图
KaiwuDB 的某个节点后台有单独的线程/ worker 保持运行,用以处理相关 Range 的分裂。Range 分裂为 2 个阶段:第 1 阶段 — Range 分裂参数准备;第 2 阶段 — 更新 Range 及其索引结构。
如图所示,左边流程主要为分裂 Range 的准备工作:
首先锁定分裂 Range 的 Key 的键值。找到这个键值后,系统会把当前的 Range 范围调整,用分裂的 Key 键值作为分区的结束键值。
流程会为右边创建一个新的 Range,它的起始键值就是分裂所用键值,结束键值即是原始 Range 的结束键值。同时,原始 Range 被分裂后,它的版本会迭代更新加 1,该更新版本会被同时应用到左右两个分裂后的 Range。
当 Range 左边和右边的分裂参数准备好后,流程进入系统数据更新环节,概括来说需要准备写请求和处理请求,该过程关联到事务,整个事务需要完成如下事项:
开始新的事务,状态为 pending
更新左边 Range
写入新的右边 Range
更新左边 Range 的巨无霸索引二层树状结构的相应查找路径
插入右边 Range 的巨无霸索引二层树状结构的相应查找路径
更新事务的状态为 Committed
更新左右 Range 的 MVCC
清理写意图
到此为止,整个 Range 的分裂完成。
分裂触发示例
如下调试触发场景是在 Range 大小超过预定的临界值后发生的。
每处理一个 Range,是按照 timer 等待串行方式进行的。在时钟唤醒后,会检查 Range 大小,如果发现这个 Range 大小在 70MB 左右,超过预定范围 64 M,就会触发系统调用分裂 Range 函数,对这个超过容量限度的 Range 进行分裂。
该后台线程或者 worker 会一直循环不断地检查所有的 Range 进行处理。
KaiwuDB Range 合并(Merge)
合并示例
如图所示,当用户删除了大量数据后,两个相邻 Range 的大小急剧变小,此时系统会将它们进行合并。
下图是合并后的效果示意,此前的两个 Range:lem-str, str- 合并后,str- 消失,只剩下 lem-,原有两个 Range 的 Key 键值均合并到同一个 Range lem-。相应的巨无霸索引二层数据结构也发生调整,一层树状索引结构里的 pea- 消失。
合并条件
合并 Range 的条件比较严格,主要包括:
合并没有被禁用
有下一个 Range 且有相同的 Configure zone
两个待合并的 Range 大小都小于 range_min_bytes
合并后不会触发 QPS 的 Range 分裂
其中最后一条是指,假如数据所在的 Range 有热点数据,即不执行合并,因为合并后又会触发分裂,反反复复将导致系统无法正常运行。
合并算法流程图
如图所示,合并与分裂一样,也是分 2 个阶段:第 1 个阶段 — Range 参数准备;第 2 阶段 — 启用事务进行 Range 更新处理。更多详情可点击视频查看完整版内容。
合并调试示例
如下两个示意图为调试 Range 合并的具体流程以及对应的 Range 相关参数。
评论