0%

redis 哈希环

本文介绍 redis 哈希环的原理

背景

我们使用一主多从的 Redis 集群存储数据,总的容量不会随从节点的增多而增多,如果容量遇到单机瓶颈,需要扩展主节点机器,让每个主节点存储一部分数据,来提高总的存储空间。假设我们现有3台主节点,我们需要使用一个散列算法把数据映射到这3台主节点上,方法如下 \[ masterId = hash(key) \% 3 \] 这种方法存在一个很严重的问题,即当容量不足时需要扩容,散列方法发生了变化,而数据还在原来的节点上,就需要对所有的数据重新散列一遍,造成大量的数据迁移,对性能产生较大影响

Redis集群能否一边迁移一边对外提供服务呢?

从读写两个角度来分析下:

读:按新的hash算法取值,没有再按旧的hash算法取值

写:按旧的hash算法写,如果没有再按新的hash算法取值

这样看,在数据迁移过程中,Redis 集群是可以对外提供读写服务的,但主要问题在于全量数据迁移导致资源和性能的浪费

为了解决这个问题,产生了哈希环的数据一致性算法

hash 环

hash 环的一致性算法会建立一个有 2^32 个槽点的环,对于一台主节点(masterA),会通过 hash(masterA) % 2^32 得到一个槽点 A,对于一个 key = K1,同样使用 hash(K1) % 2^32 映射到环上,然后以该点出发,顺时针遇到的第一个主节点,即为数据存储的主节点

hash_ring.drawio

hash 环上的数据迁移

如果新增一个节点,如 masterA 和 masterB 之间插入一个节点D,只需要迁移 A 与 B 之间的数据,而不需要全量迁移

hash_ring.drawio-1

hash 环的数据倾斜

用多台机器分别存储数据,理想情况下希望数据能均匀分布,如果 hash 环上的节点分布不均匀,都集中在一部分时,数据会倾斜到某个节点上,一方面,对某个节点的压力较大,成为系统瓶颈,另一方面,在插入/删除节点时,可能造成大量数据失效和迁移

如下图,数据倾斜在 masterB 中,masterB 的负载会明显高于其他节点;如果B失效,会有大量数据倾向到 masterA

hash_ring.drawio-2

虚拟节点

为了解决数据倾斜问题,引入了虚拟节点,比如指定每个实际节点会有 100 个虚拟节点,每个虚拟节点按照 {实际节点编号}#{虚拟节点序号} 的方式命名,如 masterA#1,然后通过散列函数映射到 hash 环上,这样会使 hash 环上的节点更多,从而减少数据倾斜程度;在操作数据时,如果命中了虚拟节点,则到对应的实际节点上执行操作