观察redis集群握手造成的带宽损耗

被槽吸引

why redis-cluster use 16384 slots? #2576
【原创】为什么Redis集群有16384个槽
看到这位博主的文章,感觉redis作者的考量很有意思。

打开集群的头文件,看了一下数据结构,不出所料是16384个槽
https://github.com/antirez/redis/blob/6.0.0/src/cluster.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*-----------------------------------------------------------------------------
* Redis cluster data structures, defines, exported API.
*----------------------------------------------------------------------------*/

#define CLUSTER_SLOTS 16384

line:248
typedef struct {
...
uint16_t type; /* Message type */
...
unsigned char myslots[CLUSTER_SLOTS/8];
...
union clusterMsgData data;
} clusterMsg;

博主解释的很挺清楚的了,说白了就是不会有这么多节点,不需要太多槽。

issue 里评论区有提到 Compress bitmap to reduce network traffic
我觉得评论区最后的回复是对的,传输中的数据没有被压缩 No compression, just 1 bit for 1 slot

追查代码:
https://github.com/antirez/redis/blob/6.0.0/src/cluster.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
line:2433
void clusterSendPing(clusterLink *link, int type) {
...
clusterBuildMessageHdr(hdr,type);
...
clusterSendMessage(link,buf,totlen);
zfree(buf);
}

line:2329
void clusterBuildMessageHdr(clusterMsg *hdr, int type) {
...
memcpy(hdr->myslots,master->slots,sizeof(hdr->myslots));
...
}

line:2292
void clusterSendMessage(clusterLink *link, unsigned char *msg, size_t msglen) {
if (sdslen(link->sndbuf) == 0 && msglen != 0)
connSetWriteHandlerWithBarrier(link->conn, clusterWriteHandler, 1);

link->sndbuf = sdscatlen(link->sndbuf, msg, msglen);
...
}

并没有发现有压缩的过程,我猜作者想说的是,压缩位图以节省内存空间

1
2
3
4
5
6
7
void clusterInit(void) {
...
line:518
/* The slots -> keys map is a radix tree. Initialize it here. */
server.cluster->slots_to_keys = raxNew();
...
}

评论区老哥

看完文章,评论区老哥的问题,引起了我的兴趣:
20200521111826.png

对呀,带宽损耗可不可以算出来?
给定节点数量、实例数量、超时时间,估算带宽损耗?
嗯,这很酷。。。

博主已经介绍了ping发送数量的计算公式:
数量=1+10*num(node.pong_received>cluster_node_timeout/2)
可对GOSSIP协议不够了解,计算结果和《Redis运维与实现》的例子对不上
200节点,20台物理机,15s超时,真的是占25Mb带宽吗?

跑一个看看

没那么多物理机,用docker做个测试,创建20个节点,分布在2个桥接的网络上:
config.png
created.png

10分钟后docker的状态
docker-stats.png

10分钟后redis-0的集群信息
redis-0-info.png

  1. 通过容器的网络IO换算一下,带宽损耗1.66Mb,例子的数据是正确的~~~
    ((121.93MB / 600s) * 1024 / 1000) * 8 = 1.66Mbps
  2. 通过redis-0的网络Output来看,每个消息大约 2.91KB/msg
    6.24MB * 1024 / 2197 = 2.91KB/msg
  3. 10分钟发送1114个ping,平均 1.86ping/s,可见公式里的 num(x) 大约是 0.086个

ping的发送

如果按本地所有节点都在默默等待超时来算,ping数量、带宽损耗会远高于观察到的结果
看源码可以知道,除了每秒固定的ping外,100ms发送ping的条件有3个

https://github.com/antirez/redis/blob/6.0.0/src/cluster.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
line:3389
/* -----------------------------------------------------------------------------
* CLUSTER cron job
* -------------------------------------------------------------------------- */

/* This is executed 10 times every second */
void clusterCron(void) {
...
line:3495
if (!(iteration % 10)) {
...
clusterSendPing(min_pong_node->link, CLUSTERMSG_TYPE_PING);
...
}
...
line:3575
if (node->link &&
node->ping_sent == 0 &&
(now - node->pong_received) > server.cluster_node_timeout/2)
{
clusterSendPing(node->link, CLUSTERMSG_TYPE_PING);
continue;
}
...
}

  1. 当前节点和目标节点的连接有效
  2. 没有向目标节点发过ping(没收到过目标节点的pong)
  3. 目标节点的本地pong接收时间已经超时

可见,我忽略了重要的第二点,即博主提到的:

在消息体中,会携带一定数量的其他节点信息用于交换。

也就是说,我向张三打听他是不是还活着,他会顺带着告诉我李四也还活着 :)

总结

由于有GOSSIP协议的信息传播,集群ping数量大幅降低


集群安装参考
https://www.runoob.com/docker/docker-redis-cluster.html
https://blog.csdn.net/alinyua/article/details/80936940
https://blog.csdn.net/u012882163/article/details/100033442