Clickhouse运维增强: 虚拟shard
书接上文, 上一篇文章提到reHash
方案的缺点在于: 停写
和数据拷贝
.
那么能不能通过数据迁移
而不是数据拷贝
的方式来处理reHash
呢?
这个时候, 就需要用到一致性Hash算法
jumpConsistentHash算法
jumpConsistentHash是一个一致性hash函数, Clickhouse本身已经实现该算法
假设value的取值范围是[0,1023), 如果shard的个数为3, 那么表达式jumpConsistentHash(value, 3)
, 能够将1024个字符, 均衡的分配到3个桶内.
而如果此时shard个数升级为4, 我们再次计算表达式jumpConsistentHash(value, 4)
, 就按照4个节点分区, 而一致性hash算法能够保证, 只会抽取前面3个节点的某些值, 放入第四个节点, 而不是在前3个节点之间, 搞数据交换.
就给我们不停服切换提供了能力.
虚拟节点
虚拟节点是一致性hash经常使用一个技术, 方便迁移时只移动某些数据.
如果所示, 整个集群有3个真实的shard, 9个虚拟的shard.
分布式写入时, 分布式表会将数据按照9份分shard, 但是[0,2,7,8]编号的数据, 实际会发送到第一个节点上.
在存储层面, DataPart级别有明确的shard编号0, 一个dataPart的数据绝对从属于一个shard
这样, 迁移的时候, 只需要做到按照DataPart级别迁移就行.
迁移过程
按照一致性hash的算法, 我们了解到DP7和DP6会迁移到新的shard之中, 这时整个变更的流程为:
- 上线新的shard
- 更新老的shard的配置, 配置时注明新加入的shard, 跟之前类似, 写入hash依然按照3处理
- 老的shard计算出自己要拷贝的shard编号(也就是6和7), 然后开始给新shard推送老的DP数据
- 老shard明确所有DP7数据已经推送到新的shard, 且自己的分布式表无数据积压, 然后将自己的分布式查询路由到新shard, 此时对于该shard来说读写为4节点, 而其他节点有可能为3节点, 因此实际上新加上的shard, 如果有数据写入, 也需要推送到对应的老shard中. 这步骤是为了做到不停写扩容, 如果可以停写的话, 只需要等待无积压即可切换路由.
- 当所有节点都是4节点配置时, 关闭数据同步的能力, 然后放开新节点的写入或者查询
- 最后再更新一下配置文件即可
整个方案的难点在于实现数据同步的能力, 类似于ReplicaMergeTree的方式.
方案评估
这个方案对于之前的提升有:
- 数据不需要拷贝了, 只需要迁移一部分数据
- 真实切换的时候, 可以做到近乎不停写
但这个方案也会带来一个比较大的问题: shard变多, DataPart个数变多, 可能对集群节点的稳定性带来较大的影响
- Shard变多, 主要风险来自于目前分布式表的实现
- DataPart变多, 风险来自于磁盘读取方面的性能