前言
在Redis Cluster集群模式中,Redis采用Sharding分片的方式,以一定的策略将数据分发存储到集群不同的节点上。
Redis Cluster主要提供两个功能
- 对数据自动分片落到各个节点上
- 即使集群中部分节点失效或者链接不上,依然可以继续处理命令
简单哈希算法
我们知道要将数据分配到不同的节点,可以简单的使用key的哈希值来求余节点的个数来实现。
假设有3个节点,则数据所在节点的位置可定位为:
L=hash(key)%3
利用这样的算法可以快速将数据定位到某个节点上。但是现假如集群容量不够了,需要增加节点,此时就要重新对已经hash过的数据重新hash去定位他的位置,显然在线上大数量的情况,很难完成数据的重定位迁移而不对实际业务产生影响。
一致性哈希算法
一致性哈希算法是由麻省理工学院于1997年提出,是一种特殊的哈希算法,目的是解决分布式场景中,添加或者移除一个服务器时,尽可能小的改变已存在的服务请求与处理请求服务之间的映射关系。其解决了简单哈希算法在分布式哈希表中存在的动态伸缩等问题。
假设哈希空间的长度是,一致性哈希算法会将整个哈希空间看成一个圆环,起点用数字0表示,终点则为
−1。
再假设我们有4台服务器,即4个节点A、B、C、D。每个节点服务器都有其对应的IP(IP不会重复),我们对节点的IP进行哈希,并对哈希结果进行取模,则每个服务器节点在圆环上都有一个其对应的位置
接下来对需要处理的数据采用如下规则确认其所在的节点。
如有数据Object A、Object B、Object C、Object D,对数据采用与节点一样的哈希算法确定其在圆环上的位置,则数据沿顺时针方向的第一个节点则为其实际存储的位置,如图所示:
算法的容错性和可扩展性
以上就是一致性哈希算法具体实现,那么其容错性和可扩展性怎样呢?
现假设Node C不幸挂掉了,那么Object C的存储就丢失,查找时它的顺时针方向找到的最新节点时Node D,也就是说Node C挂掉,影响的仅仅时Node B到Node C之间的数据,其他数据不受影响,并且节点B、C之间的数据会转移到D存储。
如果增加节点那又如何呢?
如下图,假设增加一台服务器Node X,可以看出只有Node X与延其逆时针方向的首个节点B之间的数据受影响,其他地方的数据均不受影响。
综上来看,一致性哈希算法对于节点的增减都只需要重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。
数据倾斜问题
一致性哈希算法在集群中数据节点过少时,会存在数据倾斜的问题。如下图所示,集群只有两个节点,大部分数据存储到了节点A,只有少量数据存储到了节点B。
那如何解决或者避免数据倾斜的问题呢?
我们首先想到的时增加服务器节点个数,让服务器尽量均匀的分布在哈希环上。但是实际资源可能很紧张,就只有两台服务器,那么我们如何可以让服务器多起来呢?这里我们就可以将一台物理服务器经过多个不同的哈希函数,映射虚拟出多个虚拟的服务器,多个虚拟的服务器就分布在哈希环上不同的位置,我们只需要维护好虚拟服务器与物理服务器的映射关系就可以实现‘服务器’扩充的目的,从而避免数据倾斜的问题。
如下图所示,对Node A和Node B分别虚拟出三个节点Node A#1、Node A#2、Node A#3和三个节点Node B#1、Node B#2、Node B#3,共6个节点。虚拟的节点越多,就更容易避免数据倾斜的问题。
Redis哈希槽
Redis Cluster并没有选用一致性哈希算法,而是采用了哈希槽这种概念。主要原因就是一致性哈希算法对于数据分布、节点位置的控制并不友好。
哈希槽本质上就是一个数组空间,Redis Cluster中哈希槽的其长度为 =16384,数据经过CRC16哈希算法后再取模落到16384个槽位上。
这些槽位会均匀的分配给集群中的节点,集群会记录槽和节点之间的关系。集群的高可用则是依赖节点的主从复制及主从之间的故障转移。
那为何是16384个槽,而不是其他数量的槽呢?
这是因为Redis哈希算法使用的是CRC16算法,CRC16算法产生的哈希值有16bit,那么key的哈希值分布就在0~65535之间。那为何不是用65536来取模,而使用16384呢?原因如下:
Redis集群节点之间会定期发送ping/pong消息来交换数据信息,这其中就包含节点的哈希槽配置信息slots[CLUSTER_SLOTS/8],slots[CLUSTER_SLOTS/8]其实可以看做是一个bitmap,每一位表示该槽是否属于该节点。如果槽的数量选择65535,则需要占用8kb大小,65535÷8÷1024=8kb。
如果是16348个槽,只需要占用2kb大小。消息越小传输越快,延迟就越低。另一方面从集群设计的角度权衡,集群节点数也不会超过1000个,16384个槽也能保证每个节点都会分配不少的槽数。
另外一方面CRC16将不同类型的key均匀分布在16384个槽方面表现的非常好(来自官方的测试)
参考资料
Redis:一致性Hash算法_redis一致性hash算法-优快云博客
why redis-cluster use 16384 slots? · Issue #2576 · redis/redis · GitHub
redis/src/cluster.h at ac441c741379dd4002f664c81047e8412cb793d0 · redis/redis · GitHub