一、引言
RedisCluster
目前使用的计算slot
槽位的算法为CRC16
,该算法本身会产生的hash
值的大小为16bit
,因此该算法可以产生2^16=65536
个不同的值,取值范围为0~65535
之间,从下面的代码中我们看到,目前限制的slot
槽位的个数为16384
(相关的代码为crc16(key+s+1,e-s-1) & 0x3FFF
);
/* We have 16384 hash slots. The hash slot of a given key is obtained |
那么槽位个数的时候为什么不直接采用65536呢?作者在2015年5月在相关的issue#2576上就给出了答案,作者的回复如下:
作者这句话的翻译如下所示:
原因是:
- 正常的心跳包会携带着节点的完整配置,通过使用与旧的配置信息幂等的配置来更新旧配置。 这意味着它们需要包含原始形式的节点的插槽配置信息,使用16384个slots的话将会占用2k的空间,但是如果使用65536个slots的话将会占用8k空间。
- 同时,由于其他设计权衡,RedisCluster不太可能扩展到超过1000个Master节点。
所以16384在正确的范围内可以确保每个Master有足够的插槽,最多1000个Master,但是足够小的slots数字可以很容易地将slots的配置作为原始位图数据进行传播。 请注意,在小型集群中,位图难以压缩,因为当N很小时,位图将设置slots/N位,这个是一个很大的比特集。
二、分析
依据作者给出的答案,作者吧原因定位于带宽消耗以及使用现状方面,接下来详细说明一下这样设置的原因,RedisCluster使用cluster meet <ip> <port> [bus-port]
指令将节点连接到工作集群中,例如将两个redis实例10.0.0.1:6379
和 10.0.0.2:6379
加入到集群中,我们可以使用redis-cli
连接上10.0.0.1:6379
,执行cluster meet 10.0.0.2:6379
使两个节点建立连接,后续这两个节点就会定期发送ping/pong
来交换数据信息。
这里分析一下在节点数据交换的过程中的几个重点:
- 节点交换的数据类型与大小;
- 节点数据交换的频率;
2.1、节点交换的数据类型与大小
在RedisCluster的不同节点通信过程中,会调用clusterSendPing(clusterLink *link, int type)
,依据type
区分类型,然后调用clusterBuildMessageHdr();
和clusterSendMessage();
函数构建并发送消息,下面是节点的消息类型:
/* Message types. |
2.1.1、消息头信息
在节点通信过程中,节点之间需要进行数据交换,以下结构体是节点之间进行数据交换的基础信息:
|
这里只讨论与slots相关的unsigned char myslots[CLUSTER_SLOTS/8];
这一项信息,该项为一个char
数组,这个数组实际上是一个bitmap
,这个bitmap
的每一位表示一个槽,如果对应的槽位为1
,代表对应的槽位是属于该节点的。
分析整个结构体可以看出其中空间占用最大的就是unsigned char myslots[CLUSTER_SLOTS/8];
这一项,占用空间16384/8/1024=2kb
,因此,如果槽位为65536
,发送心跳信息的消息头达8k
,发送的心跳包过于庞大;
2.1.2、消息体信息
在消息体中,会携带一定数量的其他节点信息用于交换。其他节点的信息的数量约为集群总节点数量的1/10,至少携带3个节点的信息,节点数量越多,消息体内容越大,10个节点状态下的消息体的大小大概约为1Kb左右;
具体消息体的信息是???待分析
2.2、节点数据交换的频率
Cluster
节点之间的ping/pong
操作由clusterCron()
函数不断触发,该函数每秒执行10
次,在clusterCron()
函数中,每调用10
次,执行一次Cluster
节点的ping
操作,相关代码如下:
/* 每秒执行10次 */ |
上述代码的大致逻辑是:
- 每次(秒)会随机选取
5个
节点,找出最久没有通信的节点发送ping
消息; - 每
100毫秒
(1秒10次
)都会扫描本地节点列表,如果发现节点最近一次接受pong
消息的时间大于cluster-node-timeout/2
则立刻发送ping
消息;
因此我们可以计算出每秒单节点发出的ping消息的数量为:
1+10*numOf(node.pong_received>cluster_node_timeout/2)
针对于大致的带宽消耗,《Redis的开发与运维》中在第10章 集群的集群运维 - 带宽消耗小节中,有这么一句话:
例如,一个总节点数为200的Redis集群,部署在20台物理机上每台划分10个节点,cluster-node-timeout采用默认15秒,这时ping/pong消息占用带宽达到25Mb。如果把cluster-node-timeout设为20,对带宽的消耗降低到15Mb以下。