这是本人学习的总结,主要学习资料如下
- B站狂神说,redis教程
- 马士兵教育
1、集群前置知识
1.1、数据分区策略
集群就是为了解决单机的性能瓶颈问题。
比如一个机器只能处理400条数据。如果有900条数据,那就可以用三条机器分别处理这900条数据。前三百数据给redis-1
处理,中间的三百条数据给redis-2
处理,剩下的给redis-3
处理。如下图所示。
当然上面的那种数据分区策略是很粗暴的,我们有一些更好的方式。数据分区的目的是为了让数据均匀地落在所有机器上,让每台机器不那么闲也不那么忙。
1.1.1、哈希节点取余分区
用哈希算法分区,对节点的个数取余。
比如说我们现在部署了3
台redis
服务器,此时需要存储一个数据,我们先算出数据的hash
值,然后hash%3
就可以知道数据应该存储到哪台机器上。
这个方法很容易理解但是有很大的缺点。如果遇到新增服务器的情况,那可能就面临着有大量数据需要迁移的情况。比如原本是hash%3
,增加一台机器后变成hash%4
,那很明显很多数据都需要迁移。
一般来说在这种规则下,如果要加机器,最好是加现在机器数的一倍。比如现在有3
台机器,那最好再加3
台变6
台机器。
这样加机器的话至少原本hash
值是0,1,2
的数据不用迁移,3,4,5
需要迁移而6,7,8
的数据又不需要迁移,以此往复。能让迁移的数据尽量少。
1.1.2、一致性哈希分区
首先定义key
,他是整数,大小是232。那么从0 到 232可以看成是一个圆,每个数字都是圆上的一个点。
假设现在有3
台机器,我们可以想象这3
台机器被均匀地放到圆上,redis1
放在0
的位置,redis2
放在232 / 3的位置,redis3
放在232/ 3 * 2的位置。如下图所示。
现在来了一条数据需要存储,我们计算出它的hash
值,然后得到index
= 232- hash
。如果index
落在redis1
和redis2
之间,那这数据放到redis1
中处理;如果落在redis2
和redis3
之间,那交给redis2
处理,以此类推。
这样做的好处是让数据分散地足够广,此时若有一台新节点加入,那么迁移的数据也比较少。比如现在有redis4
加入,将它放到redis2
和redis3
的中间,如下图所示。
那么现在需要迁移的数据是原本在redis2
的后半部分的数据,这些数据需要从redis2
迁移到redis4
。如果之前存储的数据足够均匀,那应该只需要迁移六分之一的数据,远远小于节点取余分区的二分之一。
这个分区算法也有一个缺点,就是新节点加入后,原本负载均衡的结构变得不平衡,redis2
和redis4
需要处理的数据是redis3
或者redis1
的一半。
1.1.3、虚拟一致性哈希分区
这种策略是一致性哈希分区的改进。在这个算法中,每个真实的节点会虚拟对应多个虚拟节点,然后这些虚拟节点再均匀分布到圆上。如果来了新数据,那新数据会使用一致性哈希分区的算法现在圆上找到对应的虚拟节点,再由虚拟节点找到真实节点。
比如redis1
有三个虚拟节点redis1#v1
,redis1#v2
和redis1#v3
,即下图中绿色的节点。而redis2
和redis3
也分别有三个虚拟节点,下图中分别用天蓝色和紫色表示。这些虚拟节点均匀交叉分布。
设置这些虚拟节点的目的是为了让每个真实节点负责的区域更碎片化。没有虚拟节点前每个节点负责的区域都是连续的大区域,真实的数据可能会集中在一小片上,这就使得节点间负载不均衡。使用虚拟节点就能很好地避免这一情况,
1.2、Redis Cluster的分区策略
Redis Cluster
的分区策略是虚拟槽分区,它是虚拟一致性哈希分区的变种。
虚拟一致性分区中一个真实节点中负责的区域都是断断续续的,而虚拟槽分区在这个基础上,再将这些断断续续的区域映射成看起来是连续的区域。
比如说,虚拟一致性分区中,redis1
负责的区域可能是[0-10]
,[50-60]
,100-110
。虚拟槽会对这三个区域再进行抽象虚拟,从外界看到的映射就是redis1
负责的是[0-30]
。
在Redis Cluster
中,所有的键根据哈希函数[CRC16[key]%16383]
映射到0-16383
的槽内。下图是三个redis
节点再槽中的映射例子。
1.3、集群的缺点
集群可以突破单机的性能瓶颈但是也存在着下面的不足。
-
键的批量操作支持有限,比如
mset, mget
某些时候会失败或者取得的值不全。
这类一次性获取多个键值对或者设置多个值的命令可能会出错。因为命令无法拆分,最终一定会落到某个机器上执行,如果要操作的键值对分布在不同的机器上,那这里的操作就可能会有意外的结果。 -
事务支持有限。
因为一个事务中可能会涉及到多个键值对的操作,而这些键值对可能分布在不同的节点上,所以集群并不总是支持事务。 -
键是数据分区的最小粒度,不能将一个很大的键值对分布在不同的节点存储。
-
有限支持
Lua
脚本和Pipeline
。 -
复制结构不支持树状结构。
集群中的主从节点要么是一主一从或者是一主多从,但不支持柱状主从。
2、集群的搭建
集群有三种搭建方式。
- 根据
redis
协议手动搭建,使用cluster meet
,cluster addslots
,cluster replication
等命令。这种是最原始的搭建方式,非常麻烦。 5.0
之前使用ruby
编写的redis-trib.rb
搭建集群,需要安装ruby
环境。5.0
之后将搭建集群的功能融进了redis-cli
命令中。因为这个最方便,所以现在都用这个方法来搭建。下面也是这个方法的示例。
2.1、配置文件设置
首先我是准备搭建一个四节点的集群,每个节点的结构是一主一从。主节点配置文件的名字中带有m
,占用端口6900~6903
。从节点名字带有s
,占用端口6930~6933
。cluster-s-6930
的主节点是cluster-m-6930
,以此类推。
下面是节点的基本配置,以6930
举例,其他配置文件类似修改对应端口号和名字即可。
port 6900
pidfile /var/run/redis_6900.pid
logfile "6900.log"
dbfilename dump6900.rdb
之后是关于集群的基本配置。将集群打开,设置日志文件等。
daemonize yes
cluster-enabled yes
cluster-config-file nodes-6900.conf
cluster-node-timeout 15000
appendonly yes
appendfilename "appendonly-6900.aof"
之后就可以依次启动所有节点
redis-server /usr/local/etc/cluster-m-6900.conf
2.2、创建集群主从节点
现在节点都启动了,但他们彼此独立,还没形成集群,下面需要让他们连接成一个集群,设置主从节点。
下面有两种方式设置主从节点,一种是随机,一种是指定。
2.2.1、随机设置主从节点
使用命令启动集群
使用命令redis-cli --cluster create 127.0.0.1:6900 127.0.0.1:6901 127.0.0.1:6902 127.0.0.1:6930 127.0.0.1:6931 127.0.0.1:6932 --cluster-replicas 1
可随机启动
redis-cli --cluster
都是关键字,其中--cluster
表示接下俩是关于集群的命令。create
,关键字,表示创建集群127.0.0.1:6900
这些都是节点地址,需要将这些地址的节点连接成集群。--cluster-replicas 1
关键字和参数,表示为每一个主节点设置一个从节点。对于六个节点的集群,这个参数会自动设置为三主三从的结构。
下面就是我将六个节点,三主三从设为集群,途中还遇到两个错误。
MacBook-Pro:~ user$ redis-cli --cluster create 127.0.0.1:6900 127.0.0.1:6901 127.0.0.1:6902 127.0.0.1:6930 127.0.0.1:6931 127.0.0.1:6932 --cluster-replicas 1
# 因为在6901的配置文件中,cluster-enabled yes这个配置没有把前面的注释符#去掉,没有开启集群设置,所以报这个错
[ERR] Node 127.0.0.1:6901 is not configured as a cluster node.
MacBook-Pro:~ user$ redis-cli --cluster create 127.0.0.1:6900 127.0.0.1:6901 127.0.0.1:6902 127.0.0.1:6930 127.0.0.1:6931 127.0.0.1:6932 --cluster-replicas 1
# 因为在6902的配置文件中,port的值设成了6302,所以没有连接成功
Could not connect to Redis at 127.0.0.1:6902: Connection refused
这时创建成功后显示的信息。从日志内容中可以看到系统配置的是三主三从的结构,6900->6931
,6901->6932
,6902->6930
。
同时也可以看到虚拟槽的分区情况。
MacBook-Pro:~ user$ redis-cli --cluster create 127.0.0.1:6900 127.0.0.1:6901 127.0.0.1:6902 127.0.0.1:6930 127.0.0.1:6931 127.0.0.1:6932 --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:6931 to 127.0.0.1:6900
Adding replica 127.0.0.1:6932 to 127.0.0.1:6901
Adding replica 127.0.0.1:6930 to 127.0.0.1:6902
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: d366c1e488c8eb328e13a830d8201ef5577b4491 127.0.0.1:6900
slots:[0-5460] (5461 slots) master
M: dba3fe3e011b1ed20152831724108c4d59ba82bb 127.0.0.1:6901
slots:[5461-10922] (5462 slots) master
M: 9858453fbf60780657ab7a0aef5a22589a84b189 127.0.0.1:6902
slots:[10923-16383] (5461 slots) master
S: cc56be7b6134ac51384504cf8db217e52ea41483 127.0.0.1:6930
replicates dba3fe3e011b1ed20152831724108c4d59ba82bb
S: 17bff3f9c621db95b81224d1c38bc02e05980c30 127.0.0.1:6931
replicates 9858453fbf60780657ab7a0aef5a22589a84b189
S: 825afbd8a9d81d5013e42ddcb791db5b7a79eca4 127.0.0.1:6932
replicates d366c1e488c8eb328e13a830d8201ef5577b4491
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:6900)
M: d366c1e488c8eb328e13a830d8201ef5577b4491 127.0.0.1:6900
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 825afbd8a9d81d5013e42ddcb791db5b7a79eca4 127.0.0.1:6932
slots: (0 slots) slave
replicates d366c1e488c8eb328e13a830d8201ef5577b4491
S: 17bff3f9c621db95b81224d1c38bc02e05980c30 127.0.0.1:6931
slots: (0 slots) slave
replicates 9858453fbf60780657ab7a0aef5a22589a84b189
S: cc56be7b6134ac51384504cf8db217e52ea41483 127.0.0.1:6930
slots: (0 slots) slave
replicates dba3fe3e011b1ed20152831724108c4d59ba82bb
M: dba3fe3e011b1ed20152831724108c4d59ba82bb 127.0.0.1:6901
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: 9858453fbf60780657ab7a0aef5a22589a84b189 127.0.0.1:6902
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.
2.2.2、主动指定节点
2.2.2.1、建立主节点
先设置一个集群,这条命令设置的集群都默认是主节点。下面是将6900
,6901
,6902
三个节点都设为主节点。
MacBook-Pro:etc user$ redis-cli --cluster create 127.0.0.1:6900 127.0.0.1:6901 127.0.0.1:6902
>>> Performing hash slots allocation on 3 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
M: d366c1e488c8eb328e13a830d8201ef5577b4491 127.0.0.1:6900
slots:[0-5460] (5461 slots) master
M: dba3fe3e011b1ed20152831724108c4d59ba82bb 127.0.0.1:6901
slots:[5461-10922] (5462 slots) master
M: 9858453fbf60780657ab7a0aef5a22589a84b189 127.0.0.1:6902
slots:[10923-16383] (5461 slots) master
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:6900)
M: d366c1e488c8eb328e13a830d8201ef5577b4491 127.0.0.1:6900
slots:[0-5460] (5461 slots) master
M: 9858453fbf60780657ab7a0aef5a22589a84b189 127.0.0.1:6902
slots:[10923-16383] (5461 slots) master
M: dba3fe3e011b1ed20152831724108c4d59ba82bb 127.0.0.1:6901
slots:[5461-10922] (5462 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
2.2.2.1.1、建立集群时的坑,集群间的通讯端口
仔细看上面创建集群时打印的日志,
我们设置的6900
,6901
,6902
端口是用于redis
与外界交互的,是客户端在redis
存取数据的。
但是建立集群后,redis
节点之间也需要通讯,这时候系统是默认给端口号加10000,就是reids
节点间的内部通讯端口。比如在这次例子中16900
,16901
,16902
就分别是三个节点的集群内部通讯端口。
很明显,如果这三个端口被占用了,那集群是无法建立成功的。不过一般不用担心,因为如果redis
被设置为cluster
节点启动时,会自动占用加1w后的端口。比如启动redis-server cluster-s-6900.conf
时,16900
会自动被占用。
如果redis
不是作为cluster
启动时(即没有cluster-enabled yes
),那加1w后的端口不会被占用。
2.2.2.2、为主节点添加从节点
可以使用下面的命令为主节点添加从节点。
redis-cli --cluster add-node 127.0.0.1:6930 127.0.0.1:6900 --cluster-slave --cluster-master-id d366c1e488c8eb328e13a830d8201ef5577b4491
。
该命令的含义是,添加127.0.0.1:6930
到127.0.0.1:6900
所属的集群中,并且作为id
为d366c1e488c8eb328e13a830d8201ef5577b4491
节点的从节点。
下面是示例
MacBook-Pro:etc user$ redis-cli --cluster add-node 127.0.0.1:6930 127.0.0.1:6900 --cluster-slave --cluster-master-id d366c1e488c8eb328e13a830d8201ef5577b4491
>>> Adding node 127.0.0.1:6930 to cluster 127.0.0.1:6900
>>> Performing Cluster Check (using node 127.0.0.1:6900)
M: d366c1e488c8eb328e13a830d8201ef5577b4491 127.0.0.1:6900
slots:[0-5460] (5461 slots) master
M: dba3fe3e011b1ed20152831724108c4d59ba82bb 127.0.0.1:6901
slots:[5461-10922] (5462 slots) master
M: 9858453fbf60780657ab7a0aef5a22589a84b189 127.0.0.1:6902
slots:[10923-16383] (5461 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 127.0.0.1:6930 to make it join the cluster.
Waiting for the cluster to join
...
>>> Configure node as replica of 127.0.0.1:6900.
[OK] New node added correctly.
之后使用cluster info
即可查看节点状态,可以看到6900
有了一个slave
节点。
MacBook-Pro:etc user$ redis-cli --cluster info 127.0.0.1:6900
127.0.0.1:6900 (d366c1e4...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:6901 (dba3fe3e...) -> 0 keys | 5462 slots | 0 slaves.
127.0.0.1:6902 (9858453f...) -> 0 keys | 5461 slots | 0 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
在这里如果没有后面的--cluster-master-id
那会随机分配各集群中的节点作为从节点。
2.2.2.1、删除集群
在创建集群时可能会遇到下面的错误
MacBook-Pro:~ user$ redis-cli --cluster create 127.0.0.1:6900 127.0.0.1:6901 127.0.0.1:6902
[ERR] Node 127.0.0.1:6900 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0
就像上面说的要么是6900
里面有数据,要么是其他节点已经知道6900
的存在。对于第二个原因多半是因为6900
和其他节点已经建立过集群,这个关系还没删除。
下面就是如何删除集群。
- 删除联系。
cluster reset
。这里只是单方面删除联系。比如说在6900
中删除6930
的联系,那只是6900
不不知道6930
的存在,但6930
可能还是依然知道6900
的存在。所以在6900
删除联系后,还需要到6930
那里删除联系。
127.0.0.1:6900> cluster reset
OK
- 检查联系:
cluster nodes
查看本节点是否还和其他节点有联系。
如下图所示,6900
知道下面5
个节点的存在(还有一个是6900
自己)。比如说,如果6900
想要和6930
建立集群关系,那么他们两者之间要互不知晓才行。
127.0.0.1:6900> cluster nodes
cc56be7b6134ac51384504cf8db217e52ea41483 127.0.0.1:6930@16930 slave dba3fe3e011b1ed20152831724108c4d59ba82bb 0 1676911335000 2 connected
dba3fe3e011b1ed20152831724108c4d59ba82bb 127.0.0.1:6901@16901 master - 0 1676911337568 2 connected 5461-10922
9858453fbf60780657ab7a0aef5a22589a84b189 127.0.0.1:6902@16902 master - 0 1676911338578 3 connected 10923-16383
d366c1e488c8eb328e13a830d8201ef5577b4491 127.0.0.1:6900@16900 myself,master - 0 1676911337000 1 connected
825afbd8a9d81d5013e42ddcb791db5b7a79eca4 127.0.0.1:6932@16932 slave d366c1e488c8eb328e13a830d8201ef5577b4491 0 1676911335000 1 connected
17bff3f9c621db95b81224d1c38bc02e05980c30 127.0.0.1:6931@16931 slave 9858453fbf60780657ab7a0aef5a22589a84b189 0 1676911336000 3 connected
我们到所有节点执行cluster reset
以后,就应该可以正常建立集群关系了。
2.3、集群添加新节点
可以使用命令redis-cli --cluster add-node 127.0.0.1:6930 127.0.0.1:6900
,这是将6903
添加到6900
所在的集群。
MacBook-Pro:etc user$ redis-cli --cluster add-node 127.0.0.1:6903 127.0.0.1:6900
>>> Adding node 127.0.0.1:6903 to cluster 127.0.0.1:6900
>>> Performing Cluster Check (using node 127.0.0.1:6900)
M: d366c1e488c8eb328e13a830d8201ef5577b4491 127.0.0.1:6900
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 825afbd8a9d81d5013e42ddcb791db5b7a79eca4 127.0.0.1:6932
slots: (0 slots) slave
replicates 9858453fbf60780657ab7a0aef5a22589a84b189
M: 9858453fbf60780657ab7a0aef5a22589a84b189 127.0.0.1:6902
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 17bff3f9c621db95b81224d1c38bc02e05980c30 127.0.0.1:6931
slots: (0 slots) slave
replicates dba3fe3e011b1ed20152831724108c4d59ba82bb
M: dba3fe3e011b1ed20152831724108c4d59ba82bb 127.0.0.1:6901
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: cc56be7b6134ac51384504cf8db217e52ea41483 127.0.0.1:6930
slots: (0 slots) slave
replicates d366c1e488c8eb328e13a830d8201ef5577b4491
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 127.0.0.1:6903 to make it join the cluster.
[OK] New node added correctly.
但是这个命令有个问题,虽然6903
被添加进集群了,但是他没有被分配槽。下面的日志可以看到,6903
是0 slots
。
MacBook-Pro:etc user$ redis-cli --cluster check 127.0.0.1:6900 --cluster-search-multiple-owners
127.0.0.1:6900 (d366c1e4...) -> 2 keys | 5461 slots | 1 slaves.
127.0.0.1:6902 (9858453f...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:6903 (cacd5f04...) -> 0 keys | 0 slots | 0 slaves.
127.0.0.1:6901 (dba3fe3e...) -> 1 keys | 5462 slots | 1 slaves.
[OK] 3 keys in 4 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 127.0.0.1:6900)
M: d366c1e488c8eb328e13a830d8201ef5577b4491 127.0.0.1:6900
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 825afbd8a9d81d5013e42ddcb791db5b7a79eca4 127.0.0.1:6932
slots: (0 slots) slave
replicates 9858453fbf60780657ab7a0aef5a22589a84b189
M: 9858453fbf60780657ab7a0aef5a22589a84b189 127.0.0.1:6902
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 17bff3f9c621db95b81224d1c38bc02e05980c30 127.0.0.1:6931
slots: (0 slots) slave
replicates dba3fe3e011b1ed20152831724108c4d59ba82bb
M: cacd5f0465133ed91e4fc659faa288651c01e88c 127.0.0.1:6903
slots: (0 slots) master
M: dba3fe3e011b1ed20152831724108c4d59ba82bb 127.0.0.1:6901
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: cc56be7b6134ac51384504cf8db217e52ea41483 127.0.0.1:6930
slots: (0 slots) slave
replicates d366c1e488c8eb328e13a830d8201ef5577b4491
这里需要手动分配槽,使用命令redis-cli --cluster reshard --cluster-from d366c1e488c8eb328e13a830d8201ef5577b4491 --cluster-to cacd5f0465133ed91e4fc659faa288651c01e88c --cluster-slots 1365 127.0.0.1:6900
,命令表示从id
为d366c1e488c8eb328e13a830d8201ef5577b4491
的节点拿出槽给id
为cacd5f0465133ed91e4fc659faa288651c01e88c
的节点,需要拿出1365
个槽,本次操作发生在127.0.0.1:6900
所在的集群中。
MacBook-Pro:etc user$ redis-cli --cluster reshard --cluster-from d366c1e488c8eb328e13a830d8201ef5577b4491 --cluster-to cacd5f0465133ed91e4fc659faa288651c01e88c --cluster-slots 1365 127.0.0.1:6900
>>> Performing Cluster Check (using node 127.0.0.1:6900)
# 省略部分日志
Ready to move 1365 slots.
Source nodes:
M: d366c1e488c8eb328e13a830d8201ef5577b4491 127.0.0.1:6900
slots:[0-5460] (5461 slots) master
1 additional replica(s)
Destination node:
M: cacd5f0465133ed91e4fc659faa288651c01e88c 127.0.0.1:6903
slots: (0 slots) master
Resharding plan:
Moving slot 0 from d366c1e488c8eb328e13a830d8201ef5577b4491
Moving slot 1 from d366c1e488c8eb328e13a830d8201ef5577b4491
# 省略部分日志
之后再查看就能看到6903
被分配了1365
个槽。
MacBook-Pro:etc user$ redis-cli --cluster check 127.0.0.1:6900 --cluster-search-multiple-owners
127.0.0.1:6900 (d366c1e4...) -> 0 keys | 4096 slots | 1 slaves.
127.0.0.1:6902 (9858453f...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:6903 (cacd5f04...) -> 2 keys | 1365 slots | 0 slaves.
127.0.0.1:6901 (dba3fe3e...) -> 1 keys | 5462 slots | 1 slaves.
[OK] 3 keys in 4 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 127.0.0.1:6900)
M: d366c1e488c8eb328e13a830d8201ef5577b4491 127.0.0.1:6900
slots:[1365-5460] (4096 slots) master
1 additional replica(s)
S: 825afbd8a9d81d5013e42ddcb791db5b7a79eca4 127.0.0.1:6932
slots: (0 slots) slave
replicates 9858453fbf60780657ab7a0aef5a22589a84b189
M: 9858453fbf60780657ab7a0aef5a22589a84b189 127.0.0.1:6902
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 17bff3f9c621db95b81224d1c38bc02e05980c30 127.0.0.1:6931
slots: (0 slots) slave
replicates dba3fe3e011b1ed20152831724108c4d59ba82bb
M: cacd5f0465133ed91e4fc659faa288651c01e88c 127.0.0.1:6903
slots:[0-1364] (1365 slots) master
M: dba3fe3e011b1ed20152831724108c4d59ba82bb 127.0.0.1:6901
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: cc56be7b6134ac51384504cf8db217e52ea41483 127.0.0.1:6930
slots: (0 slots) slave
replicates d366c1e488c8eb328e13a830d8201ef5577b4491
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Check for multiple slot owners...
2.4、集群中减少节点
如果要下线的节点有从节点,那需要先下线所有从节点,然后再下线主节点。
减少节点和增加节点一样,只不过流程是反着来。
需要先将节点拥有的槽平均分配出去,然后再删除节点。
下面是下线6903
的示例。
下面是将6903
的槽分配给6900
。
MacBook-Pro:etc fulanbin$ redis-cli --cluster reshard --cluster-from cacd5f0465133ed91e4fc659faa288651c01e88c --cluster-to d366c1e488c8eb328e13a830d8201ef5577b4491 --cluster-slots 1365 127.0.0.1:6900
先确保6903
没有槽了
MacBook-Pro:etc fulanbin$ redis-cli --cluster check 127.0.0.1:6900 --cluster-search-multiple-owners
127.0.0.1:6900 (d366c1e4...) -> 2 keys | 5461 slots | 1 slaves.
127.0.0.1:6902 (9858453f...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:6903 (cacd5f04...) -> 0 keys | 0 slots | 0 slaves.
127.0.0.1:6901 (dba3fe3e...) -> 1 keys | 5462 slots | 1 slaves.
[OK] 3 keys in 4 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 127.0.0.1:6900)
M: d366c1e488c8eb328e13a830d8201ef5577b4491 127.0.0.1:6900
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 825afbd8a9d81d5013e42ddcb791db5b7a79eca4 127.0.0.1:6932
slots: (0 slots) slave
replicates 9858453fbf60780657ab7a0aef5a22589a84b189
M: 9858453fbf60780657ab7a0aef5a22589a84b189 127.0.0.1:6902
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 17bff3f9c621db95b81224d1c38bc02e05980c30 127.0.0.1:6931
slots: (0 slots) slave
replicates dba3fe3e011b1ed20152831724108c4d59ba82bb
M: cacd5f0465133ed91e4fc659faa288651c01e88c 127.0.0.1:6903
slots: (0 slots) master
M: dba3fe3e011b1ed20152831724108c4d59ba82bb 127.0.0.1:6901
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: cc56be7b6134ac51384504cf8db217e52ea41483 127.0.0.1:6930
slots: (0 slots) slave
replicates d366c1e488c8eb328e13a830d8201ef5577b4491
然后再执行删除节点指令redis-cli --cluster del-node 127.0.0.1:6900 cacd5f0465133ed91e4fc659faa288651c01e88c
。
该命令表示删除id
为127.0.0.1:6900
的节点所在集群中,id
为cacd5f0465133ed91e4fc659faa288651c01e88c
的节点
这个示例中删除的是6903
的节点。
MacBook-Pro:etc fulanbin$ redis-cli --cluster del-node 127.0.0.1:6900 cacd5f0465133ed91e4fc659faa288651c01e88c
>>> Removing node cacd5f0465133ed91e4fc659faa288651c01e88c from cluster 127.0.0.1:6900
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
检查一下,节点确实被删除
MacBook-Pro:etc fulanbin$ redis-cli --cluster check 127.0.0.1:6900 --cluster-search-multiple-owners
127.0.0.1:6900 (d366c1e4...) -> 2 keys | 5461 slots | 1 slaves.
127.0.0.1:6902 (9858453f...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:6901 (dba3fe3e...) -> 1 keys | 5462 slots | 1 slaves.
# 省略部分日志
- 设置集群的超时时间:
redis-cli --cluster set-timeout 127.0.0.1:6900 10000
,将集群所有的超时判定设为10000
毫秒。和配置cluster-node-timeout 10000
有关。
MacBook-Pro:etc user$ redis-cli --cluster set-timeout 127.0.0.1:6900 10000
>>> Reconfiguring node timeout in every cluster node...
*** New timeout set for 127.0.0.1:6900
*** New timeout set for 127.0.0.1:6932
*** New timeout set for 127.0.0.1:6902
*** New timeout set for 127.0.0.1:6931
*** New timeout set for 127.0.0.1:6901
*** New timeout set for 127.0.0.1:6930
>>> New node timeout set. 6 OK, 0 ERR.
2.5、重定向
在集群中插入数据时会涉及到重定位的问题。我们可以连接集群中的任何一个主节点插入数据,但是数据存到哪个节点根据key
来决定。如果我们在本redis1
中插入本应该在redis2
中存储的数据,那会导致失败。
比如下面的错误表示数据应该插入到6901
中,在6900
中插入失败。
127.0.0.1:6900> set fqwetwfwqet red
(error) MOVED 6758 127.0.0.1:6901
我们可以用cluster keyslot key
知道key
的槽位。
127.0.0.1:6900> cluster keyslot redis
(integer) 1151
目前6900
管理的槽位是[0-5460]
,所以redis
这个key
可以在6900
中成功插入。
127.0.0.1:6900> set redis v1
OK
我们可以在连接reids
时指定参数,让我们能成功插入本应该由其他节点管理的数据。只需要通过redis-cli -p 6900 -c
连接即可。
下面可以看到我们在6900
中插入应由6901
管理的数据时,数据插入成功并且当前连接由6900
切换到6901
。
MacBook-Pro:etc user$ redis-cli -p 6900 -c
127.0.0.1:6900> set zzzzzzz v1
-> Redirected to slot [8897] located at 127.0.0.1:6901
OK
2.5.1、Smart客户端
集群中数据存取开销较大,如果我们在redis1
中插入应由redis2
管理的数据,那么会发送四次数据,一开始连接redis1
,二是发送执行set
指令,三是重定向连接redis2
,四是发送执行set
指令。
这种网络开销还是挺大的。为了避免这些网络开销,我们可以使用Smart客户端
。它是相当于是一个中转站,里面维护了槽位与节点之间的关系。在向节点连接执行指令之前会先判断数据应该是由哪个节点管理,之后才会发送请求。
在Java
当中,单机使用Jedis
,集群则使用JedisCluster
去完成Smart客户端
。
2.3、集群的常用命令
- 查看较详细的集群节点信息:
redis-cli --cluster check 127.0.0.1:6900 --cluster-search-multiple-owners
。
MacBook-Pro:etc user$ redis-cli --cluster check 127.0.0.1:6900 --cluster-search-multiple-owners
127.0.0.1:6900 (d366c1e4...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:6902 (9858453f...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:6901 (dba3fe3e...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 127.0.0.1:6900)
M: d366c1e488c8eb328e13a830d8201ef5577b4491 127.0.0.1:6900
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 825afbd8a9d81d5013e42ddcb791db5b7a79eca4 127.0.0.1:6932
slots: (0 slots) slave
replicates 9858453fbf60780657ab7a0aef5a22589a84b189
M: 9858453fbf60780657ab7a0aef5a22589a84b189 127.0.0.1:6902
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 17bff3f9c621db95b81224d1c38bc02e05980c30 127.0.0.1:6931
slots: (0 slots) slave
replicates dba3fe3e011b1ed20152831724108c4d59ba82bb
M: dba3fe3e011b1ed20152831724108c4d59ba82bb 127.0.0.1:6901
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: cc56be7b6134ac51384504cf8db217e52ea41483 127.0.0.1:6930
slots: (0 slots) slave
replicates d366c1e488c8eb328e13a830d8201ef5577b4491
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Check for multiple slot owners...
3、集群的原理
3.1、Gossip协议
Gossip
协议是用于集群中各节点之间的通信,其被分为四个命令ping
,pong
,meet
,fail
。这些命令会在不同的周期内向一个或多个节点发送信息到达交流的目的。
ping
:用于探查节点是否下线的,如果对面还在线则会回复一个pong
消息。频率很高,每个节点每秒会向最长时间没有联系的多个节点分别发送一次ping
消息,所以每个节点每秒会发出好几次ping
消息。pong
:用于消息确认,表示收到消息。比如一个节点收到ping
消息后会回复一个pong
表示我还在线,或者新节点加入时发送meet
消息,收到的节点会回复pong
表示收到。meet
:新节点加入时会广播发送自己已加入,收到这条信息的节点会回复pong
表示收到。fail
:当一个节点发现另一个节点下线了,它会给其他节点发送fail
消息说有个节点疑似下线。
3.2、故障转移
只针对主节点,从节点即使发现有问题也不做处理。
3.2.1、主观下线
和主从中哨兵的主观下线的概念一样,主观下线是指只有一个节点认为某个节点已下线, 客观下线是超过一定数目的节点认为某个节点已下线。
- 每个节点(比如节点A)会向最久没联系的几个节点定时发送
ping
,如果有节点(比如节点B)回复pong
那节点A就会更新与节点B的联系时间。 - 如果节点B没有发回
pong
,那一般来说节点A不会认为节点B已经下线。要等到节点A超过一定时间都没能联系到B后,A才会认为B已经下线。
3.2.2、客观下线
当节点A认为节点B主观下线以后,会开始判断其是否客观下线,具体流程如下。
节点A会持续收到其它节点的ping
消息,A会从这些ping
消息中解析其他节点认为已经下线的节点,得到有多少个节点也认为节点B已下线。如果这个数量超过总结点的一般,那就认为B客观下线。
之后向集群广播B已客观下线。
3.2.3、故障恢复
当一个节点客观下线以后,系统会尝试回复故障,简单来说会从从节点中找有资质的节点升级为主节点,代替原本故障的主节点继续工作。整个过程和非集群的故障恢复类似。具体流程如下。
- 首先会从节点的资格检查。检查从节点响应回应的时间,时间太长的就不具有资格称为主节点。和配置
cluster-node-timeout
和cluster-slave-validity-factor
有关。从节点响应的时间大于cluster-node-timeout * cluster-slave-validity-factor
的都没有资格。 - 第一轮筛选完之后就根据从节点的偏移量
offset
筛选,偏移量最大的从节点就有最新的数据,那这个从节点就更适合做主节点。 - 之后就是集群的其他主节点发起投票,如果一个从节点得到的票数过半,那就称为主节点。
- 之后是从节点升级成主节点的过程,槽的回收分配等操作。