当前位置:首页 > 问答 > 正文

Redis里怎么弄个虚拟哈希分布图,感觉挺复杂但又很关键的事儿

你想在Redis里弄个虚拟哈希分布图,这事儿感觉复杂又关键,说白了就是想解决一个问题:当你的数据多到一台Redis服务器根本存不下的时候,你怎么才能既把数据分散到多台机器上,又能快速地找到它们?这事儿是构建大型、可扩展Redis系统的核心。

最直接的想法可能是“哈希取模”,你有三台Redis服务器,编号0、1、2,当你存一个数据时,你用数据的键(key)算个哈希值,然后除以3取余数,余数是0就放服务器0,是1就放服务器1,是2就放服务器2,取数据的时候也一样算一下,就知道该去哪台机器找了,这个方法简单明了,但有个致命伤:当服务器数量变化时,灾难就来了,比如你的一台服务器扛不住了要下线,或者业务增长需要加一台新机器,服务器数量从3台变成了2台或4台,这时候,你重新计算一下就会发现,绝大部分数据的余数都变了,这意味着几乎所有的数据都要被重新搬一次家!这个数据迁移的过程工作量巨大,而且在迁移期间系统可能不可用,这显然不是我们想要的。

为了解决这个“一动就崩”的问题,有个叫“一致性哈希”的聪明想法被提了出来,这个想法在缓存系统里用得非常普遍,它的核心思路是既对数据键做哈希,也对服务器节点做哈希,然后把它们映射到同一个巨大的“哈希环”上

想象一个圆环,这个圆环的范围非常大,比如从0到2的32次方减1,也就是0到4294967295,然后首尾相连,你不再是用服务器台数取模了,而是把每台服务器的名字(ServerA”)也计算一个哈希值,这个值肯定会落在环的某个点上,假设你有三台服务器,它们就在环上占据了三个点。

Redis里怎么弄个虚拟哈希分布图,感觉挺复杂但又很关键的事儿

当你要存储一个键值对时,你还是计算这个键(key)的哈希值,也把它映射到环上,从这个键的位置开始,沿着环顺时针方向找,遇到的第一个服务器节点,就是这台数据该存放的服务器,这样一来,数据和节点就通过这个环关联起来了。

一致性哈希妙在哪里呢?妙在当节点数量变化时,它能够最大限度地减少需要移动的数据量,现在你的服务器Node2宕机了,你把它从环上拿掉,原本属于Node2的数据,现在顺时针找下一个节点,就都跑到Node3去了。只有这一部分数据需要迁移,而其他在Node1和Node3上的数据完全不受影响,同样,如果你要新增一个节点Node4,假设它被哈希后放在Node2和Node3之间,原本从Node2顺时针到Node4之间的这一部分数据,会从Node3改存到新的Node4上,其他大部分数据的位置依然不变。

你看,这样就极大地减少了因为节点增减带来的数据震荡,这也就是你提到的“虚拟哈希分布图”想要达到的核心目标:提高系统的弹性和平滑扩展能力

Redis里怎么弄个虚拟哈希分布图,感觉挺复杂但又很关键的事儿

基本的一致性哈希还有个问题:它可能做不到真正的“均匀分布”,如果节点在环上分布得不均匀,或者节点本身性能有差异(比如有的机器内存大,有的内存小),就会导致某些节点负载过重,也就是“数据倾斜”,为了解决这个问题,又引入了“虚拟节点”的概念。

“虚拟节点”是这么玩的:你不再让一个物理服务器只对应环上的一个点,而是为它分配很多个“虚拟节点”,你为性能好的服务器分配200个虚拟节点,为性能稍差的分配100个虚拟节点,每个虚拟节点都有自己的哈希值,分布在环上,数据现在映射到虚拟节点上,而虚拟节点再归属于某个物理服务器。

这样做的好处非常明显:第一,由于虚拟节点数量多,它们能把环切分得更细,分布得更均匀,从而让数据负载也更均匀地分摊到物理服务器上,第二,你可以通过调整虚拟节点的数量来平衡不同性能服务器的负载,实现加权分配,第三,当一台物理服务器下线时,它所有的虚拟节点会同时消失,这些虚拟节点上承载的数据会平滑地转移到环上其他的虚拟节点(也就是其他的物理服务器)上,同样保持了数据的均匀分布,而不会导致某台服务器瞬间压力过大。

总结一下,在Redis里要实现一个靠谱的、能应对机器扩缩容的虚拟哈希分布图,核心思想就是 “一致性哈希”加上“虚拟节点” ,虽然Redis集群模式自身已经内置实现了类似的概念来分片数据,但理解这个底层原理,对于你设计系统、排查问题至关重要,当你看到Redis集群的哈希槽(slot)时,你就可以把它理解成一种经过改良和标准化的大量“虚拟节点”,它同样是为了实现数据分片和在节点间迁移数据时尽可能小地影响服务,这事儿听起来有点绕,但一旦想通了,你就会觉得这个设计非常巧妙,它确实是构建高可用、可扩展分布式系统的关键基石。