目录
问题描述
Redis集群并没有使用一致性hash而是引入了哈希槽的概念。Redis 集群有16384个哈希槽,
每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。但为什么哈希槽的数量是16384(2^14)个呢?
CRC16算法产生的hash值有16bit,该算法可以产生2^16=65536个值。
换句话说值是分布在0~65535之间,有更大的65536不用为什么只用16384就够?作者在做mod运算的时候,为什么不mod65536,而选择mod16384? HASH_SLOT = CRC16(key) mod 65536为什么没启用
问题解释
antirez回答
https://github.com/redis/redis/issues/2576
why redis-cluster use 16384 slots? · Issue #2576 · redis/redis · GitHub
翻译
- 正常的心跳数据包带有节点的完整配置,可以用幂等方式用旧的节点替换旧节点,以便更新旧的配置。
- 这意味着它们包含原始节点的插槽配置,该节点使用2k的空间和16k的插槽,但是会使用8k的空间(使用65k的插槽)。
- 同时,由于其他设计折衷,Redis集群不太可能扩展到1000个以上的主节点。
- 因此16k处于正确的范围内,以确保每个主机具有足够的插槽,最多可容纳1000个矩阵,但数量足够少,可以轻松地将插槽配置作为原始位图传播。请注意,在小型群集中,位图将难以压缩,因为当N较小时,位图将设置的slot / N位占设置位的很大百分比。
梳理
(1)消息头控制。如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。
在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为65536时,这块的大小是: 65536÷8÷1024=8kb
在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为16384时,这块的大小是: 16384÷8÷1024=2kb
因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。
心跳包中包含节点的完整配置,如果哈希槽数为65536,心跳包的消息头大小会达到8KB(计算:65536 / 8 / 1024),而16384个槽时消息头仅为2KB。这有助于减少带宽浪费,因为Redis节点需要频繁发送心跳包。
(2)主节点数量限制。redis的集群主节点数量基本不可能超过1000个。
集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者不建议redis cluster节点数量超过1000个。 那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。
主节点数量限制 Redis集群的主节点数量不建议超过1000个,以防止网络拥堵。如果节点数在1000以内,16384个槽足够满足需求,没有必要扩展到65536个。
(3)高效的位图压缩。槽位越小,节点少的情况下,压缩比高,容易传输
Redis主节点的配置信息中它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中会对bitmap进行压缩,但是如果bitmap的填充率slots / N很高的话(N表示节点数),bitmap的压缩率就很低。 如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。
哈希槽的配置信息以位图形式保存并进行压缩。在节点少的情况下,较小的槽位数量能够提高位图的压缩率;反之,如果槽位太多而节点较少,填充率高会导致压缩效果差,增加传输负担。
总结
16384个哈希槽的设计兼顾了消息大小、节点管理和数据传输效率,使得Redis集群在保持高性能的同时,避免了带宽浪费和管理复杂性的增加。这一数量恰好符合Redis集群的使用场景,使得集群能够有效支持最多1000个主节点的配置。