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经常使用一个技术, 方便迁移时只移动某些数据.

image-20210713184924276

如果所示, 整个集群有3个真实的shard, 9个虚拟的shard.

分布式写入时, 分布式表会将数据按照9份分shard, 但是[0,2,7,8]编号的数据, 实际会发送到第一个节点上.

在存储层面, DataPart级别有明确的shard编号0, 一个dataPart的数据绝对从属于一个shard

这样, 迁移的时候, 只需要做到按照DataPart级别迁移就行.

迁移过程

image-20210713185412980

按照一致性hash的算法, 我们了解到DP7和DP6会迁移到新的shard之中, 这时整个变更的流程为:

  1. 上线新的shard
  2. 更新老的shard的配置, 配置时注明新加入的shard, 跟之前类似, 写入hash依然按照3处理
  3. 老的shard计算出自己要拷贝的shard编号(也就是6和7), 然后开始给新shard推送老的DP数据
  4. 老shard明确所有DP7数据已经推送到新的shard, 且自己的分布式表无数据积压, 然后将自己的分布式查询路由到新shard, 此时对于该shard来说读写为4节点, 而其他节点有可能为3节点, 因此实际上新加上的shard, 如果有数据写入, 也需要推送到对应的老shard中. 这步骤是为了做到不停写扩容, 如果可以停写的话, 只需要等待无积压即可切换路由.
  5. 当所有节点都是4节点配置时, 关闭数据同步的能力, 然后放开新节点的写入或者查询
  6. 最后再更新一下配置文件即可

image-20210713190606189

整个方案的难点在于实现数据同步的能力, 类似于ReplicaMergeTree的方式.

方案评估

这个方案对于之前的提升有:

  1. 数据不需要拷贝了, 只需要迁移一部分数据
  2. 真实切换的时候, 可以做到近乎不停写

但这个方案也会带来一个比较大的问题: shard变多, DataPart个数变多, 可能对集群节点的稳定性带来较大的影响

  • Shard变多, 主要风险来自于目前分布式表的实现
  • DataPart变多, 风险来自于磁盘读取方面的性能