Redis-Cluster集群创建内部细节详解
版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | https://vearne.cc
本文基于redis 6.2.7
1.引言
Redis-Cluster集群的搭建非常简单。搭建过程见参考资料1。
执行
redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \
127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
--cluster-replicas 1
root@BJ-02:~/cluster-test# redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \
> 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
> --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:7004 to 127.0.0.1:7000
Adding replica 127.0.0.1:7005 to 127.0.0.1:7001
Adding replica 127.0.0.1:7003 to 127.0.0.1:7002
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 7eb7ceb4d886580c6d122e7fd92e436594cc105e 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
M: 784fa4b720213b0e2b51a4542469f5e318e8658b 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
M: 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
S: 603a8a403536f625f53467881f5f78def9bd46e5 127.0.0.1:7003
replicates 784fa4b720213b0e2b51a4542469f5e318e8658b
S: 585c7df69fb267941a40611bbd8ed90349b49175 127.0.0.1:7004
replicates 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0
S: be905740b96469fc6f20339fc9898e153c06d497 127.0.0.1:7005
replicates 7eb7ceb4d886580c6d122e7fd92e436594cc105e
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
..
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 7eb7ceb4d886580c6d122e7fd92e436594cc105e 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 784fa4b720213b0e2b51a4542469f5e318e8658b 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 585c7df69fb267941a40611bbd8ed90349b49175 127.0.0.1:7004
slots: (0 slots) slave
replicates 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0
S: be905740b96469fc6f20339fc9898e153c06d497 127.0.0.1:7005
slots: (0 slots) slave
replicates 7eb7ceb4d886580c6d122e7fd92e436594cc105e
S: 603a8a403536f625f53467881f5f78def9bd46e5 127.0.0.1:7003
slots: (0 slots) slave
replicates 784fa4b720213b0e2b51a4542469f5e318e8658b
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
验证
root@BJ-02:~# redis-cli -h 127.0.0.1 -p 7000
127.0.0.1:7000> cluster nodes
784fa4b720213b0e2b51a4542469f5e318e8658b 127.0.0.1:7001@17001 master - 0 1652672999293 2 connected 5461-10922
7eb7ceb4d886580c6d122e7fd92e436594cc105e 127.0.0.1:7000@17000 myself,master - 0 1652672998000 1 connected 0-5460
4e0e4be1b4afd2cd1d10166a6788449dd812a4c0 127.0.0.1:7002@17002 master - 0 1652673000333 3 connected 10923-16383
585c7df69fb267941a40611bbd8ed90349b49175 127.0.0.1:7004@17004 slave 4e0e4be1b4afd2cd1d10166a6788449dd812a4c0 0 1652673000126 3 connected
be905740b96469fc6f20339fc9898e153c06d497 127.0.0.1:7005@17005 slave 7eb7ceb4d886580c6d122e7fd92e436594cc105e 0 1652673001373 1 connected
603a8a403536f625f53467881f5f78def9bd46e5 127.0.0.1:7003@17003 slave 784fa4b720213b0e2b51a4542469f5e318e8658b 0 1652673000000 2 connected
一行命令就完成了集群的部署,redis-cli --cluster create
到底做了什么。这篇文章萌叔将带你探寻其中的秘密。
2. 集群创建
首先我们来看看集群创建完毕后的基本情况
从上图,我们可以看出
redis-cli --cluster create
至少达到了3个目标
- 1.实现了集群中节点的相互发现,可以开始Gossip了
- 2.指定了它们之间的主从关系
上图中有3组实例
1) 127.0.0.1:7000(主) ~ 127.0.0.1:7005(从)
2) 127.0.0.1:7001(主) ~ 127.0.0.1:7003(从)
3) 127.0.0.1:7002(主) ~ 127.0.0.1:7004(从) - 3.确定Slot在集群中的分布
redis-cli --cluster create
对应的代码都在
clusterManagerCommandCreate中
2.1 相互发现
Sending CLUSTER MEET messages to join the cluster
发送指令CLUSTER MEET 邀请对应的节点加入集群
CLUSTER MEET first.ip first.port
客户端会向每一个节点发送(127.0.0.1:7000除外)
CLUSTER MEET 127.0.0.1 7000
这里相当于把第一个节点作为种子节点,通知给其余所有节点
如下面的简单图示,node2收到MEET消息后,会将node1加入它的cluster state
中, 然后发送PONG消息给node1
client -> {MEET node1} -> node2
① add node1 to cluster state
node1 <- PONG <- node2 ②
PING/PONG消息的构造过程,可以在clusterSendPing函数中找到。
PONG消息中包含2部分内容:
1) node2所知的所有集群节点的信息。
2) node2所负责的slot信息
消息体的定义
typedef struct {
...
unsigned char myslots[CLUSTER_SLOTS/8]; /* 本节点负责的slots信息,16384/8个char数组,一共为16384bit */
union clusterMsgData data; // 集群中节点的信息
...
} clusterMsg;
Redis的Gossip实现与标准的Gossip协议没有太大差异,它的传播速率是指数级别的。很快整个集群的所有节点的信息就能被集群中的所有节点所获知。
具体代码可以参考下面的调用关系
clusterLinkConnectHandler() -> clusterReadHandler() -> clusterProcessPacket()
2.2 指定主从关系
集群中的所有的Redis节点,哪些作为Master,哪些作为Slave都是在客户端中计算完成的。Redis使用了反亲和算法,尽量避免Master和Slave在用一台设备上
客户端下发指令CLUSTER REPLICATE,要求Slave与对应的Master进行数据同步
CLUSTER REPLICATE node-id
数据同步的细节可以阅读参考资料5
2.3 确定Slot在集群中的分布
每个Maste节点负责的Slot的范围,也是在客户端中计算得到的。
客户端下发指令CLUSTER ADDSLOTS, 告知每个Master节点,它需要负责的slot
CLUSTER ADDSLOTS slot [slot ...]
这里只有Master节点会收到指令,因为Slave会无脑的同步数据,所以无需向Slave再发送指令。在后续的Gossip中,节点能够逐渐获知其它节点所负责的范围,最终建立起对整个集群的认知。
注意:
CLUSTER ADDSLOTS
只有当某个slot没有分配时,才能生效。显然在集群刚刚创建的初始化阶段,这是毫无问题的。
额外想法
Redis Cluster集群中,Slot的后续的负载均衡和移动都需要客户端干预。萌叔认为之所以没有做成自动化的,可能是在设计者看来,redis为一个亚毫秒延迟的数据库,任何slot的迁移都会对稳定性造成影响, 那么任何迁移都应该是在集群管理员能够获知的情况下才能发生。
3.参考资料
1.Creating and using a Redis Cluster
2.CLUSTER MEET
3.一万字详解 Redis Cluster Gossip 协议
4.Gossip protocol
5.Redis 源码分析之主从复制(3)
6.CLUSTER REPLICATE
7.CLUSTER ADDSLOTS