Redis 如何路由数据

1,584 阅读5分钟

Redis Cluster

槽位映射

Redis Cluster采用虚拟哈希槽分区而非一致性hash算法,预先分配16384(img)个卡槽,所有的键根据哈希函数映射到 0 ~ 16383整数槽内,每一个分区内的master节点负责维护一部分槽以及槽所映射的键值数据。

#key到hash槽映射算法:对每个key计算CRC16值,然后对16384取模
计算公式:slot = CRC16(key) & 16383

image.png

redis cluster为什么没有使用一致性hash算法,而是使用了哈希槽预分片?

缓存热点问题:一致性哈希算法在节点太少时,容易因为数据分布不均匀而造成缓存热点的问题。一致性哈希算法可能集中在某个hash区间内的值特别多,会导致大量的数据涌入同一个节点,造成master的热点问题(如同一时间20W的请求都在某个hash区间内)

这个回答说实话个人看起来感觉有些扯淡

应该对比的是没有引入「虚拟节点」的一致性哈希算法,如果引入了虚拟节点的一致性哈希

上述的热点问题根本不存在。

同样的情况在现在的 hash 槽的情况也是存在的。因为每个 redis cluster 会等分其中的一部分区域。这一页可能会调用某个 hash 区间的值特别多导致热点问题

综合对比来看,还是引入虚拟节点的一致性哈希算法会更加优于目前的哈希槽算法

我觉得更可靠的答案还是下面:

  1. 当发生扩容时候,哈希槽采用灵活的可配置映射表,可以随意组织映射到新增server上面的slot数,比一致性hash的算法更灵活方便;同时也给开发人员手工配置更大的简洁性。
  2. 在数据迁移时,一致性hash 需要算哪些key是落在新增服务节点的数据,然后迁移这部分数据,哈希槽则直接将一个slot对应的数据全部迁移,算法明确以及实现更简单。

redis的hash槽为什么是16384(2^14)个卡槽,而不是65536(2^16)个

原作者在 issue 里面有过回答 github.com/antirez/red…

The reason is:

  • Normal heartbeat packets carry the full configuration of a node, that can be replaced in an idempotent way with the old in order to update an old config. This means they contain the slots configuration for a node, in raw form, that uses 2k of space with16k slots, but would use a prohibitive 8k of space using 65k slots.
  • At the same time it is unlikely that Redis Cluster would scale to more than 1000 mater nodes because of other design tradeoffs.

So 16k was in the right range to ensure enough slots per master with a max of 1000 maters, but a small enough number to propagate the slot configuration as a raw bitmap easily. Note that in small clusters the bitmap would be hard to compress because when N is small the bitmap would have slots/N bits set that is a large percentage of bits set.

总结起来如下:

  1. 如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。
  2. redis的集群主节点数量基本不可能超过1000个。集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者,不建议redis cluster节点数量超过1000个。 那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。
  3. 槽位越小,节点少的情况下,压缩率高。

详细解释可看 【原创】为什么Redis集群有16384个槽

客户端如何定位数据

Redis Cluster 属于服务端分片的方式。Redis 实例会把自己的哈希槽信息发给和它相连接的其它实例,来完成哈希槽分配信息的扩散。当实例之间相互连接后,每个实例就有所有哈希槽的映射关系了

  1. 客户端收到哈希槽信息后,会把哈希槽信息缓存在本地。当客户端请求键值对时,会先计算键所对应的哈希槽,然后就可以给相应的实例发送请求了。
  2. 当 Cluster 有实例增减或者负载均衡之后。实例可以通过互相传递消息来获得最新的哈希槽分配信息。而客户端需要通过 Redis Cluster 的「重定向机制」来重新获取新实例的访问地址。
MOVED重定向

image.png

Codis

槽位映射

Codis 采用 Pre-sharding 的技术来实现数据的分片, 默认分成 1024 个 slots (0-1023), 对于每个key来说, 通过以下公式确定所属的 Slot Id :

SlotId = crc32(key) % 1024

客户端请求映射

Codis 对外和客户端连接的是 Codis Proxy。采用的是客户端分片的方式:

image.png

  1. 先在 codis-proxy 计算 slot id
  2. codis proxy 会和 zk 保持信息同步。在 zk 中保存了 slot 和 group 的映射关系(zk 里面的映射关系是通过 codis dashboard 分配和修改的,一旦数据位置发生变化(例如有实例增减),路由表被修改了,codis dashbaord 就会把修改后的路由表发送给 codis proxy,proxy 就可以根据最新的路由信息转发请求了。)。
  3. 通过 slot id 找到对应的 group,将请求转发到 group 内的 Redis 实例中

参考文献