(七)Redis Cluster 集群

前言

  虽然 Redis 哨兵模式在主从模式基础上实现了高可用和读写分离功能,一定程度上提升了性能,但是此模式下集群主节点仍然只有一个,若主节点宕机了,即使从节点能够被哨兵预警要求升级为主节点,由于写入操作都是在主节点中,升级期间还是会存在数据无法写入的可能性。

  既然哨兵模式的 Redis 集群可能引发该问题,为了解决它,最好的方案是允许主节点的个数存在多个,但是,哨兵集群无法这么做!

  不过没关系,因为 Redis 3.0 后加入的 Cluster 模式的集群弥补了哨兵模式集群的不足,此模式的集群支持多个主节点。

简介

  Redis Cluster 集群采用去中心化的思想,通过数据分片(Sharding)实现数据共享功能,为了分片,Cluster 集群将所有的数据分为 16384 个 slots(槽),集群的每个节点负责其中的一部分槽位,当有 Redis 客户端连接集群时,会得到一份集群的槽位配置信息,这样它就可以直接把请求命令发送给对应的节点进行处理。

  由于 Redis Cluster 集群无代理模式去中心化的运行模式,客户端发送的绝大数命令会直接交给相关节点执行,这样大部分情况请求命令无需转发,或仅转发一次的情况下就能完成请求与响应,所以集群单个节点的性能与单机 Redis 服务器的性能是非常接近的,因此在理论情况下,当水平扩展一倍的主节点就相当于请求处理的性能也提高了一倍,所以 Redis Cluster 的性能是非常高的。

  另外,从 Redis 集群模式的演化历史(主从模式 –> 哨兵模式 –> Cluster 模式)来看,Cluster 模式的 Redis 集群架构应该会比前者更优秀,那么是这样的嘛?
  是的,你想的没错,Cluster 模式确实涵盖了主从模式中的主从复制功能和哨兵模式中的故障转移功能。

集群搭建

单节点集群

  默认情况下启动的 Redis 实例属于普通实例,普通的 Redis 实例不能成为 Redis 集群的一部分,只有作为集群节点的才可以作为集群节点启动。

  为了配置集群模式的 Redis,只需在redis.confcluster-enabled属性的前缀#注释掉:

1
2
# 开启集群模式
cluster-enabled yes

  配置完成后指定这个配置文件启动 Reids 服务即可:

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
26
27
28
29
30
31
32
$ redis-server ../conf/redis6379.conf 
35277:C 10 Feb 2022 21:41:29.793 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
35277:C 10 Feb 2022 21:41:29.793 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=35277, just started
35277:C 10 Feb 2022 21:41:29.793 # Configuration loaded
35277:M 10 Feb 2022 21:41:29.793 * Increased maximum number of open files to 10032 (it was originally set to 256).
35277:M 10 Feb 2022 21:41:29.793 * monotonic clock: POSIX clock_gettime
35277:M 10 Feb 2022 21:41:29.794 * No cluster configuration found, I'm 26af9e916cb1099482a9edea1dbd5d97d3434bc9
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 6.2.6 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in cluster mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 35277
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | https://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'

35277:M 10 Feb 2022 21:41:29.798 # Server initialized
35277:M 10 Feb 2022 21:41:29.798 * Loading RDB produced by version 6.2.6
35277:M 10 Feb 2022 21:41:29.798 * RDB age 50 seconds
35277:M 10 Feb 2022 21:41:29.798 * RDB memory usage when created 1.00 Mb
35277:M 10 Feb 2022 21:41:29.798 # Done loading RDB, keys loaded: 0, keys expired: 0.
35277:M 10 Feb 2022 21:41:29.798 * DB loaded from disk: 0.000 seconds
35277:M 10 Feb 2022 21:41:29.798 * Ready to accept connections

  查看一下该进程,发现后面多了个[cluster]的标识,即说明该 Redis 服务器开启了集群模式。

1
2
$ ps -ef|grep redis
501 35277 27886 0 9:41下午 ttys002 0:00.06 ./redis-server 127.0.0.1:6379 [cluster]

注意:Redis 服务器开启集群模式后,若要通过客户端连接,则需要使用redis-cli -c -p <port>命令。

多节点集群

  虽然刚刚我们启动了一个集群模式的节点,但是一个 Redis 集群通常由多个节点组成。因此,若要组建一个真正可以工作的集群,必须创建多个集群模式的节点,再将各个独立的节点连接起来,这样才能构成一个包含多个节点的真正意义上的集群。若不这么做的话,每个节点都相互独立,它们都处于一个只包含自己的集群当中,没有任何意义。

  那么,如何搭建多节点集群呢?

  对于 Redis Cluster 的搭建方式而言,存在以下两种:

  • 手动搭建:以配置文件的方式手动搭建 Redis 集群(生产环境)
  • 快速搭建:直接使用 Redis 安装包提供的create-cluster脚本快速搭建 Redis 集群(测试环境)

  为了快速入门,我们先使用快速搭建方式,然后去研究下这个过程中发生了什么,最后我们再以手动搭建的方式来复习巩固,结束本小节的学习。

快速搭建多节点集群

  此方式下,我们可以使用下载的 Redis 压缩包的utils/create-cluster目录下的create-cluster脚本快速搭建 Redis 集群。

  create-cluster脚本使用时需要结合相关命令,具体说明见下表:

命令 说明
start 创建集群
create 连接集群的各个节点
stop 关闭集群的各个节点
clean 清理集群

  下面我们通过这些命令来快速搭建。

  首先,我们使用./create-cluster start命令,这可以创建一个 Redis 集群,脚本过程如下:

1
2
3
4
5
6
7
8
# 创建集群
$ ./create-cluster start
Starting 30001
Starting 30002
Starting 30003
Starting 30004
Starting 30005
Starting 30006

  当然,这些节点都是单节点集群,因此接下来,我们需要将刚刚创建的 6 个节点通过./create-cluster create命令组成一个集群,脚本过程如下:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
$ ./create-cluster create
>>> 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:30005 to 127.0.0.1:30001
Adding replica 127.0.0.1:30006 to 127.0.0.1:30002
Adding replica 127.0.0.1:30004 to 127.0.0.1:30003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 9cd97b0ac5a1bf4bd6efad9d732b897a558de7e2 127.0.0.1:30001
slots:[0-5460] (5461 slots) master
M: c8394345033517c9bb4ad9fb22b2fca2e01d773f 127.0.0.1:30002
slots:[5461-10922] (5462 slots) master
M: ca5a91c0311482c0eb675b95fca790965889633e 127.0.0.1:30003
slots:[10923-16383] (5461 slots) master
S: b92f248feec5fbedef47f0c18fcda1ff5c88f512 127.0.0.1:30004
replicates ca5a91c0311482c0eb675b95fca790965889633e
S: 0d2b8955e09a01def8a1259dda363ac286f34d3c 127.0.0.1:30005
replicates 9cd97b0ac5a1bf4bd6efad9d732b897a558de7e2
S: 2084fc7dada43737440ecd5067a097eb5a8d9c9d 127.0.0.1:30006
replicates c8394345033517c9bb4ad9fb22b2fca2e01d773f
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:30001)
M: 9cd97b0ac5a1bf4bd6efad9d732b897a558de7e2 127.0.0.1:30001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 2084fc7dada43737440ecd5067a097eb5a8d9c9d 127.0.0.1:30006
slots: (0 slots) slave
replicates c8394345033517c9bb4ad9fb22b2fca2e01d773f
S: b92f248feec5fbedef47f0c18fcda1ff5c88f512 127.0.0.1:30004
slots: (0 slots) slave
replicates ca5a91c0311482c0eb675b95fca790965889633e
M: c8394345033517c9bb4ad9fb22b2fca2e01d773f 127.0.0.1:30002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: ca5a91c0311482c0eb675b95fca790965889633e 127.0.0.1:30003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 0d2b8955e09a01def8a1259dda363ac286f34d3c 127.0.0.1:30005
slots: (0 slots) slave
replicates 9cd97b0ac5a1bf4bd6efad9d732b897a558de7e2
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

  在执行的过程中,将询问你集群能否使用以下配置:

  • 将 30001、30002、30003 节点的实例作为集群的主节点
  • 将 30004、30005、30006 节点的实例作为集群的从节点
  • [0-5460] 的槽分配给 30001 节点
  • [5461-10922] 的槽分配给 30002 节点
  • [10923-16383] 的槽分配给 30003 节点

  输入 yes 将遵循以上配置继续执行。

  最后等以上脚步执行完成,我们就可以使用redis-cli -c -p [Port]连接到集群,比如:

1
$ redis-cli -c -p 30001

清理集群

  当测试完成之后,为了释放资源,可以使用以下命令,关闭并清理集群:

1
2
3
4
5
6
7
8
9
10
# 关闭集群
$ ./create-cluster stop
Stopping 30001
Stopping 30002
Stopping 30003
Stopping 30004
Stopping 30005
Stopping 30006
# 清理集群
$ ./create-cluster clean

认识槽的概念

  在搭建集群的过程中,我们发现该脚本会自动分配主从节点,那么其内部做了什么,主从节点分别有什么特性,这个我们暂表不谈,后面再进行探究,而先了解下什么是槽。

  在搭建集群的过程中,我们发现存在一个槽的分配过程,那么:

  • 槽是什么?
  • 槽的作用是什么?
  • 如何分配槽?
  • 什么时候分配槽?
槽的作用

  由于Redis 集群是通过分片的方式来保存数据库中的键值对,为了对数据进行分片,引入了槽的概念。
  对槽而言,存在以下作用:

  • 划分数据库:将 Redis 集群的整个数据库分为 16384 个可用插槽(slot),集群中的每个主节点可以处理 0 个或最多 16834 个插槽
  • 映射键:往 Redis 集群中写入的每个键都将存储到 16834 个插槽中的一个当中
  • 控制集群状态
    • 当数据库中的 16834 个槽都有节点在处理时,集群处于上线状态ok
    • 若数据库中有任何一个槽没有得到处理,集群就处于下线状态fail
槽的分配

  为了使 Redis 集群正常运行,我们需要给集群的各个节点分配槽。
  槽的分配方式存在两种:

  • 手动分配:使用redis-cli -h <IP> -p <Port> cluster addslots {0..5460}可以手动分配槽,不建议使用
  • 自动分配:使用redis-cli --cluster reshard 127.0.0.1:6379命令可自动分配槽,前面的脚本也是这么做的
槽分配的时间点

  当集群中的节点变化(新增或移除)时,需要重新对槽进行分配,这样才能保证整个数据库的数据均匀

槽的计算

  当往集群中设置一个键值对时,会使用公式CRC16(key) % 16384来计算键key属于哪个槽, 其中CRC16(key)语句用于计算键keyCRC16校验和。

手动搭建多节点集群

  虽然create-cluster脚本搭建的方式速度很快,但是,由于此方式搭建的集群安装在同一台服务器上,所以只能用于测试环境。

  在实际的生产环境中,我们还是需要使用手动添加配置的方式搭建 Redis 集群。

  下面,我们就来搭建一下吧。

机器规划

主机 redis 路径
10.211.55.15 ~/software/redis
10.211.55.19 ~/software/redis
10.211.55.20 ~/software/redis

目录准备

1
2
3
4
mkdir -p software/redis
cd software/redis
mkdir -p redis6379/{conf,data,log}
mkdir -p redis7379/{conf,data,log}

上传文件

  上传下载的redis-6.2.6.tar.gz文件到用户~/software/redis目录并解压:

1
tar -zxvf redis-6.2.6.tar.gz

编译安装

1
2
3
4
5
6
cd ~/software/redis/redis-6.2.6/src
make
# 指定 bin 目录安装路径
make PREFIX=~/software/redis install
# 不指定则脚本将安装到 /usr/local/bin 下
# make install

文件配置

  复制配置文件到主从节点目录:

1
2
cp ~/software/redis/redis-6.2.6/redis.conf ~/software/redis/redis6379/conf/redis6379.conf
cp ~/software/redis/redis-6.2.6/redis.conf ~/software/redis/redis7379/conf/redis7379.conf

修改配置文件(主节点)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
cd ~/software/redis/redis6379/conf/
vim redis6379.conf

# 注释掉此行
#bind 127.0.0.1 -::1
# 开启守护进程
daemonize yes
dir "../redis6379/data"
logfile "../redis6379/log/log.log"
# 设置密码
masterauth 123456
requirepass 123456
# 开启集群模式
cluster-enabled yes
修改配置文件(从节点)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cd ~/software/redis/redis7379/conf/
vim redis7379.conf

# 注释掉此行
#bind 127.0.0.1 -::1
port 7379
# 开启守护进程
daemonize yes
dir "../redis7379/data"
logfile "../redis7379/log/log.log"
# 设置密码
masterauth 123456
requirepass 123456
# 开启集群模式
cluster-enabled yes

启动 Redis

1
2
3
4
5
cd ~/software/redis/bin
# 分别启动三台 6379 节点:
./redis-server ../redis6379/conf/redis6379.conf
# 分别启动三台 7379 节点:
./redis-server ../redis7379/conf/redis7379.conf

检查服务状态

  检查是否启动成功:

1
2
3
4
[root@redis bin]# ps -ef | grep redis
root 21802 1 0 05:39 ? 00:00:04 ./redis-server *:7379 [cluster]
root 22909 1 0 06:11 ? 00:00:00 ./redis-server *:6379 [cluster]
root 22922 21412 0 06:13 pts/0 00:00:00 grep --color=auto redis

连接集群分配槽

  之前说过:若要组建一个真正可以工作的集群,必须:

  • ① 创建多个集群模式的节点
  • ② 再将各个独立的节点连接起来

  只有经过以上两个步骤才能构成一个包含多个节点的真正意义上的集群。若不这么做的话,每个节点都相互独立,它们都处于一个只包含自己的集群当中,没有任何意义。

  刚刚我们才完成了第一步,那么接下来就将各个节点连接起来吧!

1
2
# --cluster-replicas 1 意味着我们希望每个创建的主服务器都有一个从服务器 -a 指定密码 
./redis-cli --cluster create 10.211.55.15:6379 10.211.55.19:6379 10.211.55.20:6379 10.211.55.15:7379 10.211.55.19:7379 10.211.55.20:7379 --cluster-replicas 1 -a 123456

  当我们执行上述命令之后,日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 10.211.55.19:7379 to 10.211.55.15:6379
Adding replica 10.211.55.20:7379 to 10.211.55.19:6379
Adding replica 10.211.55.15:7379 to 10.211.55.20:6379
M: 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379
slots:[0-5460] (5461 slots) master
M: 91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379
slots:[5461-10922] (5462 slots) master
M: 928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379
slots:[10923-16383] (5461 slots) master
S: 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379
replicates 928ccd9535a16c4de172dfccf481172a63f01646
S: 843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379
replicates 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5
S: ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379
replicates 91a7eece0873a6f1e648b0b109f57da558e25eb6
Can I set the above configuration? (type 'yes' to accept): yes

  翻译过来就是:集群接受使用以下配置嘛?

  • 将 10.211.55.15:6379、10.211.55.19:6379、10.211.55.20:6379 节点的实例作为集群的主节点
  • 将 10.211.55.15:7379、10.211.55.19:7379、10.211.55.20:7379 节点的实例作为集群的从节点
  • [0-5460] 的槽分配给 10.211.55.15:6379 节点
  • [5461-10922] 的槽分配给 10.211.55.19:6379 节点
  • [10923-16383] 的槽分配给 10.211.55.20:6379 节点

  输入 yes 将遵循以上配置继续执行。

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
26
27
28
>>> 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 10.211.55.15:6379)
M: 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379
slots: (0 slots) slave
replicates 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5
S: 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379
slots: (0 slots) slave
replicates 928ccd9535a16c4de172dfccf481172a63f01646
S: ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379
slots: (0 slots) slave
replicates 91a7eece0873a6f1e648b0b109f57da558e25eb6
M: 928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

  执行上述流程,槽就帮我们自动分配好了。

可能出现的 Bug

  若出现Could not connect to Redis No route to host,可以尝试关闭防火墙:

1
systemctl stop firewalld.service

测试

  当集群搭建完成后,我们测试下集群是否正常:

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
26
27
./redis-cli -h 10.211.55.19 -p 6379 -c -a 123456
# 查看集群信息
10.211.55.19:6379> cluster info
cluster_state:ok # 状态 --> 正常
cluster_slots_assigned:16384 # 已分配槽位数
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6 # 节点数
cluster_size:3 # 主节点数
cluster_current_epoch:6
cluster_my_epoch:2
cluster_stats_messages_ping_sent:640
cluster_stats_messages_pong_sent:635
cluster_stats_messages_meet_sent:1
cluster_stats_messages_sent:1276
cluster_stats_messages_ping_received:635
cluster_stats_messages_pong_received:641
cluster_stats_messages_received:1276
# 查看集群节点信息
10.211.55.19:6379> cluster nodes
843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379@17379 slave 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 0 1644581800070 1 connected
91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379@16379 myself,master - 0 1644581792000 2 connected 5461-10922
2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379@16379 master - 0 1644581801112 1 connected 0-5460
8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379@17379 slave 928ccd9535a16c4de172dfccf481172a63f01646 0 1644581802172 3 connected
ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379@17379 slave 91a7eece0873a6f1e648b0b109f57da558e25eb6 0 1644581800603 2 connected
928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379@16379 master - 0 1644581800604 3 connected 10923-16383

弹性伸缩

  虽然我们前面手动搭建了实际的生产环境集群,但是,若遇到业务变化,需要提升用户体验或减少服务器运行成本,那么就不得不增加或减少 Redis 节点了,这个时候就又需要使用一些命令来进行调整了。

增加主节点

  下面我们新增一台10.211.55.22主机,按前面的设置配置好集群模式的 Redis 节点,然后准备将其加入前面我们手动搭建的集群中。

cluster meet 方式

  cluster meet ip port命令可将一个节点加入到集群中,执行过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 进入原集群
./redis-cli -h 10.211.55.19 -p 6379 -c -a 123456
# 添加新节点到集群中
10.211.55.19:6379> cluster meet 10.211.55.22 6379
OK
# 查看集群节点信息
10.211.55.19:6379> cluster nodes
843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379@17379 slave 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 0 1644584726978 1 connected
91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379@16379 myself,master - 0 1644584726000 2 connected 5461-10922
2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379@16379 master - 0 1644584729098 1 connected 0-5460
8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379@17379 slave 928ccd9535a16c4de172dfccf481172a63f01646 0 1644584728028 3 connected
b8fe42263de0a829b80e83cfdce99d4d5f2dbbee 10.211.55.22:6379@16379 master - 0 1644584726000 0 connected
ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379@17379 slave 91a7eece0873a6f1e648b0b109f57da558e25eb6 0 1644584730170 2 connected
928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379@16379 master - 0 1644584731232 3 connected 10923-16383

  可以10.211.55.22 6379的 Redis 节点加入到集群当中了,并被设置成了主节点。

add-node 方式(推荐)

  redis-cli --cluster add-node也可以把一个节点添加到集群中,其后接参数描述如下:

  • new_host:new_port existing_host:existing_port:把新节点加入到指定的集群,默认添加为主节点
  • --cluster-slave:新节点作为从节点,默认随机一个主节点
  • --cluster-master-id [NodeId]:指定新节点跟随的主节点nodeId

  执行过程如下:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 增加 10.211.55.22:6379 节点到 10.211.55.19:6379 所在集群中
./redis-cli -a 123456 --cluster add-node 10.211.55.22:6379 10.211.55.19:6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Adding node 10.211.55.22:6379 to cluster 10.211.55.19:6379
>>> Performing Cluster Check (using node 10.211.55.19:6379)
M: 91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379
slots: (0 slots) slave
replicates 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5
M: 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379
slots: (0 slots) slave
replicates 928ccd9535a16c4de172dfccf481172a63f01646
S: ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379
slots: (0 slots) slave
replicates 91a7eece0873a6f1e648b0b109f57da558e25eb6
M: 928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 10.211.55.22:6379 to make it join the cluster.
[OK] New node added correctly.

# 进入集群
./redis-cli -h 10.211.55.19 -p 6379 -c -a 123456
# 查看集群节点信息
10.211.55.19:6379> cluster nodes
843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379@17379 slave 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 0 1644585701944 1 connected
91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379@16379 myself,master - 0 1644585701000 2 connected 5461-10922
2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379@16379 master - 0 1644585703010 1 connected 0-5460
8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379@17379 slave 928ccd9535a16c4de172dfccf481172a63f01646 0 1644585700901 3 connected
b8fe42263de0a829b80e83cfdce99d4d5f2dbbee 10.211.55.22:6379@16379 master - 0 1644585697730 0 connected
ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379@17379 slave 91a7eece0873a6f1e648b0b109f57da558e25eb6 0 1644585698794 2 connected
928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379@16379 master - 0 1644585700000 3 connected 10923-16383

  从以上结果可以看出10.211.55.22:6379节点也被设置成了主节点。

增加从节点

cluster replicate 方式

  cluster replicate nodeId命令就可以把当前节点设置为目标节点的从节点,执行过程如下:

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
26
27
28
29
30
31
# 进入原集群
./redis-cli -h 10.211.55.19 -p 6379 -c -a 123456
# 添加新节点到集群中
10.211.55.19:6379> cluster meet 10.211.55.22 7379
OK
# 查看集群节点信息
10.211.55.19:6379> cluster nodes
843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379@17379 slave 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 0 1644586033523 1 connected
91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379@16379 myself,master - 0 1644586025000 2 connected 5461-10922
2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379@16379 master - 0 1644586036660 1 connected 0-5460
8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379@17379 slave 928ccd9535a16c4de172dfccf481172a63f01646 0 1644586034559 3 connected
69bdaa09bb1fe2c9f55322a4eb7b4e4f069f67bc 10.211.55.22:7379@17379 master - 0 1644586035603 7 connected
b8fe42263de0a829b80e83cfdce99d4d5f2dbbee 10.211.55.22:6379@16379 master - 0 1644586036551 0 connected
ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379@17379 slave 91a7eece0873a6f1e648b0b109f57da558e25eb6 0 1644586031000 2 connected
928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379@16379 master - 0 1644586032485 3 connected 10923-16383

# 进入当前节点
./redis-cli -h 10.211.55.22 -p 7379 -c -a 123456

# 将当前节点设置为 10.211.55.22:6379 的从节点
10.211.55.22:7379> cluster replicate b8fe42263de0a829b80e83cfdce99d4d5f2dbbee
OK
10.211.55.22:7379> cluster nodes
ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379@17379 slave 91a7eece0873a6f1e648b0b109f57da558e25eb6 0 1644660840793 2 connected
b8fe42263de0a829b80e83cfdce99d4d5f2dbbee 10.211.55.22:6379@16379 master - 0 1644660838269 0 connected
91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379@16379 master - 0 1644660840289 2 connected 5461-10922
2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379@16379 master - 0 1644660841803 1 connected 0-5460
843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379@17379 slave 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 0 1644660838774 1 connected
928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379@16379 master - 0 1644660837764 3 connected 10923-16383
69bdaa09bb1fe2c9f55322a4eb7b4e4f069f67bc 10.211.55.22:7379@17379 myself,slave b8fe42263de0a829b80e83cfdce99d4d5f2dbbee 0 1644660830000 0 connected
8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379@17379 slave 928ccd9535a16c4de172dfccf481172a63f01646 0 1644660839784 3 connected

add-node 方式(推荐)

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 增加 10.211.55.22:7379 节点到 10.211.55.19:6379 所在集群中,并且设置为从节点
./redis-cli -a 123456 --cluster add-node 10.211.55.22:7379 10.211.55.19:6379 --cluster-slave --cluster-master-id b8fe42263de0a829b80e83cfdce99d4d5f2dbbee
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Adding node 10.211.55.22:7379 to cluster 10.211.55.19:6379
>>> Performing Cluster Check (using node 10.211.55.19:6379)
M: 91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379
slots: (0 slots) slave
replicates 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5
M: 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379
slots: (0 slots) slave
replicates 928ccd9535a16c4de172dfccf481172a63f01646
M: b8fe42263de0a829b80e83cfdce99d4d5f2dbbee 10.211.55.22:6379
slots: (0 slots) master
S: ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379
slots: (0 slots) slave
replicates 91a7eece0873a6f1e648b0b109f57da558e25eb6
M: 928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 10.211.55.22:7379 to make it join the cluster.
Waiting for the cluster to join

>>> Configure node as replica of 10.211.55.22:6379.
[OK] New node added correctly.

# 进入集群
./redis-cli -h 10.211.55.19 -p 6379 -c -a 123456
# 查看集群节点信息
10.211.55.19:6379> cluster nodes
843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379@17379 slave 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 0 1644585979929 1 connected
91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379@16379 myself,master - 0 1644585974000 2 connected 5461-10922
2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379@16379 master - 0 1644585983083 1 connected 0-5460
8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379@17379 slave 928ccd9535a16c4de172dfccf481172a63f01646 0 1644585982003 3 connected
69bdaa09bb1fe2c9f55322a4eb7b4e4f069f67bc 10.211.55.22:7379@17379 slave b8fe42263de0a829b80e83cfdce99d4d5f2dbbee 0 1644585976759 0 connected
b8fe42263de0a829b80e83cfdce99d4d5f2dbbee 10.211.55.22:6379@16379 master - 0 1644585977000 0 connected
ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379@17379 slave 91a7eece0873a6f1e648b0b109f57da558e25eb6 0 1644585980947 2 connected
928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379@16379 master - 0 1644585980031 3 connected 10923-16383

  可以看到,10.211.55.22:7379节点成功增加到了10.211.55.19:6379所在集群中,并且被设置为10.211.55.22:6379的从节点

删除节点

cluster forget 方式

  cluster forget nodeId命令可把一个节点从集群中移除。

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
26
27
28
29
30
31
32
33
34
35
36
37
# 连接集群
./redis-cli -h 10.211.55.19 -p 6379 -a 123456 -c
# 查看集群节点信息
10.211.55.19:6379> cluster nodes
843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379@17379 slave 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 0 1644587256348 13 connected
91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379@16379 myself,slave ede1046e421d6f020464b1c2c13ef460efdbe632 0 1644587249000 18 connected
2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379@16379 master - 0 1644587259524 13 connected 0-5460
8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379@17379 master - 0 1644587255281 17 connected 10923-16383
69bdaa09bb1fe2c9f55322a4eb7b4e4f069f67bc 10.211.55.22:7379@17379 slave b8fe42263de0a829b80e83cfdce99d4d5f2dbbee 0 1644587252653 0 connected
b8fe42263de0a829b80e83cfdce99d4d5f2dbbee 10.211.55.22:6379@16379 master - 0 1644587257395 0 connected
ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379@17379 master - 0 1644587258455 18 connected 5461-10922
928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379@16379 slave 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 0 1644587256557 17 connected

# 从集群中删除主节点
10.211.55.19:6379> cluster forget b8fe42263de0a829b80e83cfdce99d4d5f2dbbee

# 查看删除后的集群信息
10.211.55.19:6379> cluster nodes
843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379@17379 slave 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 0 1644587399413 13 connected
91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379@16379 myself,slave ede1046e421d6f020464b1c2c13ef460efdbe632 0 1644587396000 18 connected
2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379@16379 master - 0 1644587400559 13 connected 0-5460
8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379@17379 master - 0 1644587400452 17 connected 10923-16383
69bdaa09bb1fe2c9f55322a4eb7b4e4f069f67bc 10.211.55.22:7379@17379 slave - 0 1644587395277 7 connected
ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379@17379 master - 0 1644587398346 18 connected 5461-10922
928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379@16379 slave 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 0 1644587397274 17 connected

# 从集群中删除从节点
10.211.55.19:6379> cluster forget 69bdaa09bb1fe2c9f55322a4eb7b4e4f069f67bc
OK
# 查看删除后的集群信息
10.211.55.19:6379> cluster nodes
843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379@17379 slave 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 0 1644587732430 13 connected
91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379@16379 myself,slave ede1046e421d6f020464b1c2c13ef460efdbe632 0 1644587731000 18 connected
2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379@16379 master - 0 1644587735581 13 connected 0-5460
8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379@17379 master - 0 1644587733473 17 connected 10923-16383
ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379@17379 master - 0 1644587734526 18 connected 5461-10922
928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379@16379 slave 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 0 1644587730000 17 connected

  此种方式删除节点后节点会重新加入集群,为了将一个节点完全的从集群中删除,必须对集群中其他所有节点都发送cluster forget命令,操作比较麻烦。

del-node 方式(推荐)

  redis-cli --cluster del-node命令也可以把一个节点从集群中移除,并且节点不会重新加入集群。

  此命令执行过程如下:

1
2
3
4
5
./redis-cli -a 123456 --cluster del-node 10.211.55.19:6379 b8fe42263de0a829b80e83cfdce99d4d5f2dbbee
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Removing node b8fe42263de0a829b80e83cfdce99d4d5f2dbbee from cluster 10.211.55.19:6379
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.

重新分片

  刚刚,我们往 3 主 3 从的 Redis 集群中增加了一个主节点和一个从节点,现在集群的信息时如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 连接到集群中
./redis-cli -h 10.211.55.19 -p 6379 -c -a 123456

# 查看节点信息
10.211.55.19:6379> cluster nodes
843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379@17379 slave 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 0 1644585979929 1 connected
91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379@16379 myself,master - 0 1644585974000 2 connected 5461-10922
2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379@16379 master - 0 1644585983083 1 connected 0-5460
8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379@17379 slave 928ccd9535a16c4de172dfccf481172a63f01646 0 1644585982003 3 connected
69bdaa09bb1fe2c9f55322a4eb7b4e4f069f67bc 10.211.55.22:7379@17379 slave b8fe42263de0a829b80e83cfdce99d4d5f2dbbee 0 1644585976759 0 connected
b8fe42263de0a829b80e83cfdce99d4d5f2dbbee 10.211.55.22:6379@16379 master - 0 1644585977000 0 connected
ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379@17379 slave 91a7eece0873a6f1e648b0b109f57da558e25eb6 0 1644585980947 2 connected
928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379@16379 master - 0 1644585980031 3 connected 10923-16383

  从信息可以看出,添加的主节点10.211.55.22:6379有一个从节点10.211.55.22:7379,但是并没有分配任何槽位,这显然是不能满足我们的需求的。

  没有给主节点分配槽位,此节点就不处理任何数据,为了使得此节点能够处理数据,我们需要重新划分槽位,让数据均匀的分布在所有的主节点上,这样才能发挥集群的最大作用。

  那么,如何重新划分槽位呢?
  在 Redis 的集群中,提供了重新分片功能,重新分片操作可以将任意数量已经指派给某个节点(源节点)的槽指派给另一个节点(目标)节点,并且相关槽所属的键值对也会从源节点被移动到目标节点。

  若想使用重新分片操作,需要使用cluster reshard命令:

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
26
27
28
29
30
31
32
./redis-cli -a 123456 --cluster reshard 10.211.55.19:6379

>>> Performing Cluster Check (using node 10.211.55.19:6379)
M: 91a7eece0873a6f1e648b0b109f57da558e25eb6 10.211.55.19:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 843a4c78eda20686dac725e973b01ef95de58980 10.211.55.19:7379
slots: (0 slots) slave
replicates 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5
M: 2f4630f64cbfdb1f7da1439dba5350a6a5f752c5 10.211.55.15:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be 10.211.55.15:7379
slots: (0 slots) slave
replicates 928ccd9535a16c4de172dfccf481172a63f01646
S: 69bdaa09bb1fe2c9f55322a4eb7b4e4f069f67bc 10.211.55.22:7379
slots: (0 slots) slave
replicates b8fe42263de0a829b80e83cfdce99d4d5f2dbbee
M: b8fe42263de0a829b80e83cfdce99d4d5f2dbbee 10.211.55.22:6379
slots: (0 slots) master
1 additional replica(s)
S: ede1046e421d6f020464b1c2c13ef460efdbe632 10.211.55.20:7379
slots: (0 slots) slave
replicates 91a7eece0873a6f1e648b0b109f57da558e25eb6
M: 928ccd9535a16c4de172dfccf481172a63f01646 10.211.55.20:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 4096

  在执行的过程中,它会询问你打算移动多少个节点,取值范围是 1 到 16384,我们输入 4096,即移动 4096 个槽位到某个主节点,输入命令之后,流程继续执行:

1
What is the receiving node ID? b8fe42263de0a829b80e83cfdce99d4d5f2dbbee

  其次,它会询问你需要把这些槽位分配到哪个节点上,请输入节点 ID,我们10.211.55.19:6379节点对应的 Id 输入进去,流程继续执行:

1
2
3
4
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1: all

  然后,它会询问你要从那个源节点中进行转移,我们输入all命令,即从所有节点中随机抽取,流程继续执行:

1
2
3
4
5
6
7
8
    Moving slot 12281 from 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be
Moving slot 12282 from 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be
Moving slot 12283 from 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be
Moving slot 12284 from 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be
Moving slot 12285 from 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be
Moving slot 12286 from 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be
Moving slot 12287 from 8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be
Do you want to proceed with the proposed reshard plan (yes/no)? yes

  最后,它会将所有要转移的节点信息列举出来,让你进行确认,我们需要输入 yes ,之后就开始执行转移操作了。

1
2
3
4
5
...
Moving slot 12284 from 10.211.55.15:7379 to 10.211.55.22:6379:
Moving slot 12285 from 10.211.55.15:7379 to 10.211.55.22:6379:
Moving slot 12286 from 10.211.55.15:7379 to 10.211.55.22:6379:
Moving slot 12287 from 10.211.55.15:7379 to 10.211.55.22:6379:

  当分片分配完毕之后,可使用cluster slots命令来查看槽位的相关信息:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 进入集群
./redis-cli -h 10.211.55.19 -p 6379 -c -a 123456

# 查看集群槽位信息
10.211.55.19:6379> cluster slots
1) 1) (integer) 0
2) (integer) 1364
3) 1) "10.211.55.22"
2) (integer) 6379
3) "b8fe42263de0a829b80e83cfdce99d4d5f2dbbee"
4) 1) "10.211.55.22"
2) (integer) 7379
3) "69bdaa09bb1fe2c9f55322a4eb7b4e4f069f67bc"
2) 1) (integer) 1365
2) (integer) 5460
3) 1) "10.211.55.15"
2) (integer) 6379
3) "2f4630f64cbfdb1f7da1439dba5350a6a5f752c5"
4) 1) "10.211.55.19"
2) (integer) 7379
3) "843a4c78eda20686dac725e973b01ef95de58980"
3) 1) (integer) 5461
2) (integer) 6826
3) 1) "10.211.55.22"
2) (integer) 6379
3) "b8fe42263de0a829b80e83cfdce99d4d5f2dbbee"
4) 1) "10.211.55.22"
2) (integer) 7379
3) "69bdaa09bb1fe2c9f55322a4eb7b4e4f069f67bc"
4) 1) (integer) 6827
2) (integer) 10922
3) 1) "10.211.55.19"
2) (integer) 6379
3) "91a7eece0873a6f1e648b0b109f57da558e25eb6"
4) 1) "10.211.55.20"
2) (integer) 7379
3) "ede1046e421d6f020464b1c2c13ef460efdbe632"
5) 1) (integer) 10923
2) (integer) 12287
3) 1) "10.211.55.22"
2) (integer) 6379
3) "b8fe42263de0a829b80e83cfdce99d4d5f2dbbee"
4) 1) "10.211.55.22"
2) (integer) 7379
3) "69bdaa09bb1fe2c9f55322a4eb7b4e4f069f67bc"
6) 1) (integer) 12288
2) (integer) 16383
3) 1) "10.211.55.15"
2) (integer) 7379
3) "8a1d60fc8d0af615a7ab7fe43a5820419b6fc2be"
4) 1) "10.211.55.20"
2) (integer) 6379
3) "928ccd9535a16c4de172dfccf481172a63f01646"

  从结果可以看出, 集群的原来三个主节点都抽取了一部分槽位,分配给了10.211.55.22:6379节点。

扩展

  重新分片操作可以在线进行,因此执行操作集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。

注意:重新分片过程中可能出现 ASK 错误,即客户端发送获取键的命令的时候,该键所处的槽正在从一个节点迁移到另一个节点,就会报 ASK 错误,不过节点会根据这个错误提供的 IP 和端口号转向目标节点发送ASKING命令,之后再重新发送原本想要执行的命令,所以问题不大。。。

负载均衡

  在 Redis 集群负载不均衡的情况下,为了使得各个节点的负载压力趋于平衡,从而提高 Redis 集群的整体运行效率,我们可以使用--cluster rebalance命令重新分配各个节点负责的槽数量,

1
./redis-cli -a 123456 --cluster rebalance 10.211.55.19:6379

注意哦:有时候输入--cluster rebalance命令时系统可能不会执行,因为系统认为当前分配没有优化的必要,所以不会进行分配,此时会直接退出,如下所示:

1
2
3
4
5
6
7
./redis-cli -a 123456 --cluster rebalance 10.211.55.19:6379
>>> Performing Cluster Check (using node 10.211.55.19:6379)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
*** No rebalancing needed! All nodes are within the 2.00% threshold.

故障应对

故障检测

  集群中的每个节点都会定期地向集群中的其他节点发送PING消息,以此来检测对方是否在线
  若接收PING消息的节点没有在规定时间内向发送PING的节点返回PONG消息,接收节点就会被发送节点标记为疑似下线
  若在一个集群里面,半数以上的负责处理槽的主节点都将某个主节点标记为疑似下线,那么这个主节点将被标记为已下线,并向集群广播一条关于该节点的FALL消息。

故障转移

  当一个从节点发现自己正在复制的主节点进入了已下线状态时,从节点将开始对下线主节点进行故障转移,会执行以下操作:

  • ① 从下线主节点的所有从节点基于一定规则选中一个执行slaveof no one命令进行选举,选举出新的主节点;
  • ② 新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己;
  • ③ 新的主节点会向集群广播一条PONG消息,让其他节点立即知道当前节点由从节点变为了主节点,并且新主已接管所有旧主负责处理的槽;
  • ④ 新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成

选举规则

  故障转移时会选举出新的主节点,选举的规则如下:

  • 集群的节点都有一个属性——纪元(epoch),它是一个自增计数器,初始值为 0;
  • 当集群里的某个节点开始一次故障转移操作时,集群配置纪元的值会被增一
  • 对于每个配置纪元,集群里每个负责处理槽的主节点都有一次投票的机会,而第一个向主节点要求投票的从节点将获得主节点的投票
  • 当从节点发现自己正在复制的主节点进入已下线状态时,从节点会向集群广播一条特殊的消息,要求所有收到这条消息、并且具有投票权的主节点向这个从节点投票
  • 若一个主节点具有投票权(它正在负责处理槽),并且这个主节点尚未投票给其他从节点,那么主节点将会回应要求投票的从节点发送的消息,主节点将返回一条确认消息给从节点,表示这个主节点支持从节点成为新的主节点
  • 每个参与选举的从节点都会接收其他主节点回应的确认消息,并根据自己收到了多少条这种消息来统计自己获得了多少主节点的支持
  • 若集群里有 N 个具有投票权的主节点,那么当一个从节点收集到半数以上(大于等于N/2+1张)支持票时,这个从节点就会当选为新的主节点
  • 因为在每一个配置纪元里面,每个具有投票权的主节点只能投一次票,所以如果有 N 个主节点进行投票,那么具有半数以上(大于等于N/2+1张)支持票的从节点只会有一个,这确保了新的主节点只会有一个
  • 若在一个配置纪元里面没有从节点能收集到足够多的支持票,那么集群进入一个新的配置纪元,并再次进行选举,直到选出新的主节点为止

  这个选举新主节点的方法和哨兵模式选举领头 Sentinel 的方法非常相似,因为两者都是基于 Raft 算法的领头选举( leader election)方法来实现的。

扩展——数据迁移

  随着业务扩展,需要使用性能更高的机器去替换旧的机器,那么,Redis 是否支持此操作?

  自然是支持的。利用CLUSTER FAILOVER命令可以手动让集群中的某个master宕机,切换到执行CLUSTER FAILOVER命令的slave节点,实现无感知的数据迁移。

  因此,只要我们将高性能的机器作为集群中某个主机的从机,之后执行CLUSTER FAILOVER命令,即可平滑升级机器。

工作流程

  CLUSTER FAILOVER命令执行的流程原理如下:

  • ① slave 节点通知 master 节点拒绝任何客户端请求
  • ② master 返回当前的数据 offset 给 slave
  • ③ slave 等待数据 offset 与 master 保持一致
  • ④ slave 和 master 开始进行故障转移
  • ⑤ slave 标记自己为 master,并广播故障转移的结果
  • ⑥ master 收到广播,降级为从机,之后只处理客户端读请求

支持模式

  CLUSTER FAILOVER命令支持三种不同模式:

  • 缺省:默认的流程,如图 ① ~ ⑥ 步
  • force:省略对 offset 的一致性校验
  • takeover:直接执行第 ⑤ 步,忽略数据一致性、忽略 master 状态和其它 master 的意见

最佳实践

  集群还是主从?

  集群虽然具备高可用特性,能实现自动故障恢复,但是如果使用不当,也会存在一些问题。

  由于单体 Redis、主从 Redis、已经能达到万级别的 QPS,并且主从加哨兵集群也具备很强的高可用特性。

  因此,如果主从能满足业务需求的情况下,尽量不搭建 Redis Cluster 集群。

集群完整性问题

  在Redis的默认配置中,如果发现任意一个插槽不可用,则整个集群都会停止对外服务:

1
cluster-require-full-coverage yes

  为了保证高可用特性,建议将cluster-require-full-coverage配置为no

集群带宽问题

  集群节点之间会不断的互相 Ping 来确定集群中其它节点的状态。每次 Ping 携带的信息至少包括:

  • 插槽信息
  • 集群状态信息

  集群中节点越多, 集群状态信息数据量也越大,10 个节点的相关信息可能达到 1kb, 此时每次集群互通需要的带宽会非常高。

  解决途径:

  • ① 配置合适的cluster-node-timeout
  • ② 避免在单个物理机中运行太多的 Redis 实例
  • ③ 避免大集群,集群节点数不要太多, 最好少于 1000,如果业务庞大,则需要拆分子业务,为各个子业务建立对应的集群

数据倾斜问题

  待补充。

客户端性能问题

  待补充。

命令的集群兼容性问题

  待补充。

lua 和事务问题

  待补充。

扩展

节点结构

  节点的信息都将保存在自身的一个数据结构——clusterNode中。
  简单来说,clusterNode结构保存了一个节点的当前状态,比如:

  • 节点的名字
  • 节点的创建时间
  • 节点当前的配置纪元
  • 节点的 IP 地址和端口号
  • 节点处理的槽等等信息

槽的数据结构

  不同的节点都保存了一个clusterNode结构,该结构中的slotsnumslot属性负责记录了节点复制处理哪些槽。
  slots属性是一个二进制数组,这个数组长度为 16384 / 8 = 2048 字节,共包含 16384 个二进制位。

  Redis 以 0 为起始索引,16383 为终止索引,对slots数组中的 16384 个二进制位进行编号,并根据索引i上的二进制位的值来判断节点是否负责处理槽i:

  • 如果slots数组在索引i上的二进制位的值为 1,那么表示节点负责处理槽i
  • 如果slots数组在索引i上的二进制位的值为 0,那么表示节点不负责处理槽i

  下图展示了一个slots数组示例:这个数组索引 0 至索引 7 上的二进制位的值都为 1,其余所有二进制位的值都为 0,这表示此节点负责处理槽 0 至槽 7。

slots 数组示例一

  下图展示了另一个slots数组示例:这个数组索引 1、3、5、8、9、10 上的二进制位的值都为 1,而其余所有二进制位的值都为 0,这表示节点负责处理槽 1、3、5、8、9、10。

slots 数组示例二

  一个节点除了会将自己负责处理的槽信息记录在 2 个属性当中,还会将自己的数组通过消息发送给集群中的其他节点,以此来告知其他节点自己目前负责处理哪些槽。

节点差异

类型差异

  Redis 集群中的节点分为主节点(master)和从节点(slave),其中:

  • 主节点:用于处理槽
  • 从节点:用于复制某个主节点(从节点不含槽),并在被复制的主节点下线时,代替下线主节点继续处理命令请求

数据库差异

  集群节点保存键值对以及键值对过期时间的方式和单机 Redis 服务器保存键值对以及键值对过期时间的方式完全相同,此处不再介绍。
  节点和单机数据库在数据库方面的一个区别是:节点只能使用 0 号数据库,而单机 Redis 服务器则没有这一限制。

节点握手

  客户端连接节点后向另一个节点发送该命令可以让这两个节点进行握手,握手成功后,另一个节点就会被添加到发起命令节点所在的集群中,过程如下图:

节点握手为集群

节点通信

  集群中的各个节点通过发送和接收消息来进行通信,发送消息的节点为发送者,接收消息的节点为接收者。
  节点发送的消息主要为以下五种:

  • MEET 消息:当发送者接到客户端发送的cluster meet命令时,发送者会向接收者发送MEET消息,请求接收者加入到发送者当前所处集群里;
  • PING 消息:集群的每个节点默认每隔 1 秒钟就会从已知节点列表随机挑选 5 个节点,然后对这 5 个节点中最长时间没有发送过PING消息的节点发送PING消息,以此来检测被选中节点是否在线,其他特殊情况也会发送该消息;
  • PONG 消息:接收者确认MEETPING消息已到达时,会向发送者发送该消息告知。特殊情况如故障转移成功后会向集群广播一条该消息;
  • FALL 消息:当一个主节点 A 判断另一个主节点 B 已经进入FALL状态时,节点 A 会向集群广播B的FALL消息,所有收到这条消息的节点都会立即将节点 B 标记为已下线;
  • PUBLISH 消息:当节点接收到一个publish命令时,节点会执行这个命令,并向集群广播一条PUBLISH消息,所有接收到这条消息的节点都会执行相同的publish命令。

集群中执行命令的过程

  在对数据库中的 16384 个槽都进行了指派之后,集群就会进入上线状态,这时客户端就可以向集群中的节点发送数据命令了。

  当客户端向节点发送与数据库键有关的命令时(如set username lucy),接受命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽是否指派给了自己。

  比如我们在之前10.211.55.15:637910.211.55.19:637910.211.55.20:6379 三个主节点组成的集群中,用客户端连接节点10.211.55.15:6379主节点,执行下面的命令:

1
2
10.211.55.15:6379> set b 1
OK

  因为键b所在的槽正是节点10.211.55.15:6379负责处理的,所以执行命令成功,若不是呢?
  比如键username不是当前节点能处理的,它会说该键由 14315 槽处理,而该槽需要在节点10.211.55.20:6379 才能处理,所以会重定向到10.211.55.20:6379节点进行处理,代码如下:

1
2
3
10.211.55.15:6379> set username lucy
-> Redirected to slot [14315] located at 10.211.55.20:6379
OK

小结

  通过本文,我们首先探究了 Redis Cluster 的作用,其相当于把 Redis 的主从复制和哨兵监控功能整合到了一起,之后,我们通过脚本快速搭建了一个 Redis Cluster 模式的集群,对集群内部的一些相关概念进行了学习,由于快速搭建的集群不适用于生产环境,我们又手动搭建了一个生产环境的集群,并根据生产环境可能的业务变更学习了水平伸缩重新分片负载均衡相关概念,最后,我们学习了一下集群应对故障的措施。

思维导图

参考

  • 黄健宏 Redis 设计与实现 [M]. 机械工业出版社,2014

文章信息

时间 说明
2020-04-16 完稿
2022-06-21 部分重构
2022-12-22 增加数据迁移一节
0%