36. 集群同步的关键因素
Redis Cluster保存的数据量和支撑的吞吐量是和集群本身的实例数量有关联的,一个集群官方建议上限为1000个实例
而这个上限1000的原因,是因为Redis Cluster在消息同步上的去中心化
Redis Cluster的去中心化如何影响到实例之间通信的呢?这就是今天我们所要讲述的
Redis Cluster运行中,每个实例都会保存Slot和实例的对应关系,以及自身的状态信息
为了方便同步协议,Redis采用业界常用的Gossip协议
Gossip协议即八卦协议,会让每个实例按照一定的频率,从集群中挑选出一定的实例,让彼此进行PING 和 PONG,PING消息和PONG消息中封装的消息是相同的,利用彼此的推拉消除消息差异
但是从上面可以看出,PING和PONG消息会伴随着集群规模的扩大而发送的越多
那么我们先看每次PING PONG发送的消息格式和对应的消息大小
PING消息的整体结构如下
typedef struct {
char nodename[CLUSTER_NAMELEN]; //40字节 uint32_t ping_sent; //4字节 uint32_t pong_received; //4字节 char ip[NET_IP_STR_LEN]; //46字节 uint16_t port; //2字节 uint16_t cport; //2字节 uint16_t flags; //2字节 uint32_t notused1; //4字节 } clusterMsgDataGossip; |
整体大小相加为104个字节
而且外加上,每个实例发送Gossip消息的时候,还会传递集群十分之一实例的状态信息
所以,对于一个1000个实例的集群来说,每个实例发送一个PING消息的时候,总的数据量为10400个字节,大约是10KB
而且方便Slot的传递,PING消息还包含了一个16384bit的Bitmap,每一个都对应了一个Slot,如果值为1,就说明Slot属性当前实例,16384bit约为2KB,这样相加之后,一个PING的大小约为12KB,一来一回就有24KB了
这样会降低集群服务正常客户端的吞吐量
接下来我们再说一下实例之间通信的频率
Redis Cluster实例会每秒从实例列表中挑选出5个实例,并找出一个最久没有通信的实例,将PING消息发给这个实例
除了每秒的扫描,Redis Cluster 还会按照每100ms一次的频率,扫描本地的实例列表,如果有实例最近一次接收PONG消息的时间,已经大于cluster-node-timeout的一般了,就会发送PING消息,更新这个实例上的集群状态信息
那么每秒发送的PING消息数量为
PING消息发送数量 = 1 + 10 * 实例数(超过cluster-node-timeout/2)
伴随着集群数量的增大,超过cluster-node-timeout的实例可能会越来越多
假设集群中有30个实例出现这种问题,就会占用36MB/s带宽,挤占集群中正常服务的带宽
那么我们如何降低实例之间的通信开销?
原理上说,可以减小实例传输的消息大小,但是这个是写死的,需要修改源码
其次是考虑降低实例之间的发送频率,那么这个频率主要存在两个,一个是每1秒发送一条PING消息,这个频率不算高,再降低这个频率的话,集群中的各实例状态就没法及时传播了
每个实例每100毫秒会进行一次检测,超过cluster-node-timeout/2的节点发送PING消息,这个cluster-node-timeout是可以配置的,其表示为一个实例被判断为故障从而下线的时间,默认为15S,如果这个值小,就会比较频繁出现PONG消息接收超时的情况,从而导致实例每秒要执行10次给PONG消息超时的实例发送PING消息的操作
调的太大会影响到Redis Cluster故障恢复时间
总结一下:
我们说了Redis Cluster实例之间以Gossip协议进行通信的机制,Redis Cluster运行的时候,需要通过PING PONG进行消息交换,心跳消息包含了当前实例和其他部分实例的状态信息,以及Slot分配信息,有助于Redis Cluster中的实例拥有完整集群状态
伴随着集群规模的扩大,实例之间的通信也会增加,如果盲目的对Redis Cluster进行扩容,会导致大规模实例之间进行ping-pong导致集群无法处理正常请求的带宽
对于这个问题,可以调整cluster-node-timeout配置项减少心跳消息的占用带宽请求
最后,一个建议,最好将Redis Cluster规模控制在400-500个实例
假设每个主实例配置一个从实例,400-500实例可以支持1600-2000万QPS,就可以满足不少业务的需求
一个小问题,采用跟Codis保存Slot分配信息相似的方法,将实例保存在第三方的存储上,例如ZK,会有什么影响吗?
好处在于,这样减少了每个实例的负担,保证了元信息的一致性
坏处是集群系统中需要额外维护第三方系统,增加了系统复杂度