图2 如上图所示,将树形结构从一层拓展成二层,如果继续拆分新的叶子Group,则可以将树形结构拓展到三层,拆分方案可以支持到十层。叶子 Group是物理分片,直接对应的 Redis 实例,分支 Group 是虚拟分片,当Hash 命中到分支 Group 后,并没有找不到对应的Redis实例,需要再继续向下寻找,直到找到叶子 Group 为止。
图3 CRedis水平分拆上线后,DBA将现存的绝大部分超过15G的实例都拆分成更小的实例,在一段时间内缓解了大内存实例的运维治理压力。但随着Redis规模的快速增长,不断有大的实例集群出现,此外CRedis水平分拆的缺点也逐渐暴露出来: 1)持续的周期很长,对多个 Group 进行拆分的话,每个Group的数据需要同时复制几份同样的实例。比如60G的某个实例(图3),如果想拆到5G一个,那么下级的Group必须有12个,而拆分要先将该实例的数据先同步为12个60G的实例,再根据key的命中规则清理该12个60G的实例中不会命中的key,最终演变成12个5G的实例。一般60G的group实例拆分需要3个小时-6个小时,如果一个集群的分片非常多,加上观察对业务影响的时间,可能要持续上几天或一两周,并且只能是有人值守的串行操作。 2)拆分过程中需要2次迁移,如上面所说的,拆分中中间态实例对于内存的要求是非常大的,拆分完成后对内存的需求会急剧下降,因此每次拆分都涉及到2次迁移,尽管迁移不会影响业务,但对于执行操作拆分的运维人员来说,心智负担比较大,而且一不小心也会导致线上事故。 3)拆分后无法还原回去,也就是说假设业务分拆后收缩,对Redis的需求变小了,但它实际拆分后的分片还在那边,所申请的空间还并没有释放掉,客观上浪费了资源,降低了Redis总体的利用率。 4)只支持扩容,不支持缩容,这点上面也提到了,除了一些集群过大需要分拆外,还有一些申请远超需求的实例需要缩容,而水平分拆对于这点无能为力。 5)拆分一次,就多一次的性能损耗,因为需要多计算一次hash,虽然耗时不大,但是对于性能敏感的业务还是有影响。 由此可见,水平分拆的方案虽然解决了实例过大的问题,但不能缩容的弊端也逐渐凸现了出来。尤其是在今年因疫情影响需要降本增效的背景下,一方面资源比充足,一方面宿主机上跑的都是无法缩容的实例。那么是否有更好的解决方案呢?答案是有的。