Fork me on GitHub

版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | 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


微信公众号

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注