16384,别问为啥,问就是 2K!
官方回答
CRC16 算法产生的值有 16 位,能产生 2^16 也就是 65536 个值,那为什么 Redis 在集群模式下要把槽数固定为 16384,这个问题有人在 Redis 的 Github 仓库中提过 Issue,开发者也给了解答:
理解一下就是:
- Redis 节点的心跳数据中包含槽的数据,一个槽作为一个 bit,16384 个 bit 相当与 2048 个 Byte 也就是 2KB 的大小,而 65536 个槽的大小为 8K
- Redis 集群节点数量不会超过 1000 个,超过可能引发拥堵,因为要同步信息的节点太多
- Redis Master 的配置信息中负责的哈希槽通过 Bitmap 来维护,传输过程中需要对 Bitmap 进行压缩,节点数为 N,Bitmap 压缩率就为 slots/N,如果 slots 太多的话那么压缩率就很低
Redis 的心跳数据
截至目前 Redis 最新的稳定版本为 6.2,找一下 Github 仓库中 6.2 版本的集群文件 cluster.h 源码,路径为:https://github.com/redis/redis/blob/6.2/src/cluster.h
typedef struct {
char sig[4]; /* Signature "RCmb" (Redis Cluster message bus). */
uint32_t totlen; /* Total length of this message */
uint16_t ver; /* Protocol version, currently set to 1. */
uint16_t port; /* TCP base port number. */
uint16_t type; /* Message type */
uint16_t count; /* Only used for some kind of messages. */
uint64_t currentEpoch; /* The epoch accordingly to the sending node. */
uint64_t configEpoch; /* The config epoch if it's a master, or the last
epoch advertised by its master if it is a
slave. */
uint64_t offset; /* Master replication offset if node is a master or
processed replication offset if node is a slave. */
char sender[CLUSTER_NAMELEN]; /* Name of the sender node */
unsigned char myslots[CLUSTER_SLOTS/8]; /* 这里,节点负责的槽位 */
char slaveof[CLUSTER_NAMELEN];
char myip[NET_IP_STR_LEN]; /* Sender IP, if not all zeroed. */
char notused1[32]; /* 32 bytes reserved for future usage. */
uint16_t pport; /* Sender TCP plaintext port, if base port is TLS */
uint16_t cport; /* Sender TCP cluster bus port */
uint16_t flags; /* Sender node flags */
unsigned char state; /* Cluster state from the POV of the sender */
unsigned char mflags[3]; /* Message flags: CLUSTERMSG_FLAG[012]_... */
union clusterMsgData data;
} clusterMsg;
在结构体 clusterMsg 中有一个 myslots 的 char 数组 unsigned char myslots[CLUSTER_SLOTS/8]
,长度为 16384/8 = 2048,除以 8 的原因是在 C 语言里一个 char 占一个 Byte 也就是 8 bit,这个 char 数组组成一张 Bitmap,每一个 bit 表示一个槽,如果某个 bit 为 1,就表示这个 bit 对应的槽归这个 master 管,如果槽位为 65536,每次的 ping 消息的消息头太大了,浪费带宽
总的来说,从心跳数据大小、节点数、网络带宽、消息压缩率等多方面考虑,Redis 的开发者认为 16384 个槽位能满足日常业务需求并且数量是比较合适的