Redis 系列(4)— 切片集群

系列专栏:Redis系列专栏

切片集群

数据扩展模式

如果Redis要缓存的总数据量不是很大,比如5GB数据,一般使用 主从模型 + 哨兵集群保证高可用 就可以满足。但如果Redis要缓存的总数据量比较大,或者未来可能会增大,比如20GB、50GB数据,那一个主库就无法满足了,这时一般有两种模式来扩展:纵向扩展和横向扩展。

纵向扩展

纵向扩展就是升级单个Redis实例的配置,增加服务器内存容量、磁盘容量、使用更高配置的CPU。比如之前使用的 4C 8GB 50GB ,升级到 8C 32GB 100GB,数据量再增多,就继续加配置。

纵向扩展的好处是实施起来简单,但也存在潜在问题:

  • 如果使用了 RDB 数据持久化,随着数据量增加,一方面需要的内存也会增加;另一方面在RDB持久化时,主线程会 fork 子进程,而 fork 操作的耗时和 Redis 的数据量是正相关的,数据量越大,fork 操作造成的主线程阻塞的时间就越长,就会导致Redis有时响应变慢。当然,如果不需要RDB持久化就不存在这个问题。

  • 另一个问题是纵向扩展会受到硬件和成本的限制,把内存从 32GB 扩展到 64GB 还算容易,但是,要想扩充到 1TB,就会面临硬件容量和成本上的限制了。

横向扩展

横向扩展就是增加 Redis 实例的个数,将数据分散到各个实例上,比如要缓存15GB的数据,使用3台8GB的服务器就可以,一个实例只缓存5GB的数据即可。

这就是Redis的 切片集群,也叫分片集群,就是启动多个 Redis 实例组成一个集群,然后按照一定的规则,把收到的数据分到各个实例中去。这种方案只用增加 Redis 的实例个数就行了,不用担心单个实例的硬件和成本限制。

虽然组建切片集群比较麻烦,但是它可以保存大量数据,由于单个实例保存的数据量较小,RDB 持久化时对主线程的阻塞影响也比较小。而且随着用户或业务规模的扩展,保存大量数据的情况通常是无法避免的,那切片集群就是一个非常好的解决方案。

扫描二维码关注公众号,回复: 13776305 查看本文章

集群架构

Redis 的部署一般根据不同的场景有不同的方案,下面做个简单的对比:

  • 1、单机模式

如果数据量很少,主要是承载 高并发高性能 的场景,比如缓存一般就几个G,部署一个单机Redis实例就足够了。

  • 2、主从复制+哨兵

这种模式主要是保证 高并发读和高可用 的场景,主从模式就是 一个主库+多个从库,部署几个从库则跟 读吞吐量 有关系,然后再部署一个哨兵集群来保证 主从集群 的高可用性。一般在数据量不是特别大的场景下使用这种模式即可。

  • 3、集群模式

Redis主从架构模式下,所有数据都写入一个主库,存在着单机容量上限、高并发写性能的一些问题。Redis 集群则有多个主库,数据是根据一定规则分散到各个主库上的,主库的数量就取决于数据量的大小,可以根据数据量动态增删主库节点。

Redis 集群至少需要 3 个主节点才可以正常运转,每个主节点建议再配置至少一个从节点,用于做主备切换,在主库不可用时,从库可以顶上去。生产环境中,建议使用 6 台服务器,分别部署 3 个主库和 3 个从库,这样更能保证集群的高可用。

image.png

不同于主从架构,Redis 集群中的从节点主要是做高可用的,每个主库挂一两个从节点,用来做数据的热备、主备切换,实现高可用,因此集群模式中的主从节点不需要哨兵来保证高可用。

另外,如果要提升高并发读的性能,一般可以通过增加主库节点来提升读吞吐量,当然也可以通过修改配置将从库配置为可读,做读写分离模式,不过这种可能会复杂一点,同时Redis客户端也支持读写分离才行。

集群部署

首先停止之前部署的 Redis 主从和哨兵集群,Redis 集群的部署方式与之前不一样。然后根据上面那张集群架构图,我们使用三台服务器部署6个实例,即三主三从,并且尽量让 主从实例不在同一台机器上。

规划6个实例部署到三台服务器上,6个实例的端口依次为 7001 ~ 7006,部署架构如下:

服务器 Redis 实例
172.17.0.2 7001、7002
172.17.0.3 7003、7004
172.17.0.4 7005、7006

1、创建数据目录

首先在第一台服务器上操作,先创建几个目录:

# mkdir -p /var/redis/log
# mkdir -p /var/redis/7001
# mkdir -p /var/redis/7002
复制代码

2、配置文件

将Redis配置文件拷贝两份到 /etc/redis 下,分别为 7001.conf、7002.conf:

# cp /usr/local/src/redis-6.2.5/redis.conf /etc/redis/7001.conf
# cp /usr/local/src/redis-6.2.5/redis.conf /etc/redis/7002.conf
复制代码

修改配置文件中的如下配置,7001 与文件端口一致即可:

配置 说明
bind 172.17.0.2 绑定本机IP
port 7001 端口
cluster-enabled yes 开启集群模式
cluster-config-file /etc/redis/node-7002.conf 集群配置文件
cluster-node-timeout 15000 节点存活超时时长
daemonize yes 守护进程
pidfile /var/run/redis_7001.pid PID文件
dir /var/redis/7001 数据目录
logfile /var/redis/log/7001.log 日志文件
appendonly yes 开启AOF持久化

注意不要在文件中配置 replicaof <masterip> <masterport>

3、启动脚本

将Redis启动脚本复制两份到 /etc/init.d/下:

# cp /usr/local/src/redis-6.2.5/utils/redis_init_script /etc/init.d/redis_7001
# cp /usr/local/src/redis-6.2.5/utils/redis_init_script /etc/init.d/redis_7002
复制代码

然后修改脚本中的端口号:

image.png

并在脚本开头添加如下两行注释做开机启动:

# chkconfig: 2345 90 10 
# description: Redis is a persistent key-value database
复制代码

设置开机启动:

# chkconfig redis_7001 on
# chkconfig redis_7002 on
复制代码

4、启动Redis实例

分别安装上面的方式在三台服务器上加好配置文件和启动脚本,然后分别启动各个实例。

# cd /etc/init.d

# ./redis_7001 start
# ./redis_7002 start
复制代码

查看Redis进程:

image.png

如果启动不成功可以查看日志:

image.png

5、创建集群

集群管理可以使用官方提供的 redis-cli --cluster 工具,命令格式为:

redis-cli --cluster SUBCOMMAND [ARGUMENTS] [OPTIONS]
复制代码

创建集群的命令格式如下,--cluster-replicas <arg> 表示每个 master 有几个 slave:

redis-cli --cluster create host1:port1 ... hostN:portN --cluster-replicas <arg>
复制代码

下面将6个实例创建一个集群,它会自动帮我们分配哪些实例作为 master,哪些作为 slave,并尽量让 master 和 slave 不在同一个服务器上。通过输出结果可以看到集群创建的过程。

[root@centos-01 init.d]# redis-cli --cluster create 
    172.17.0.2:7001 172.17.0.2:7002 
    172.17.0.3:7003 172.17.0.3:7004 
    172.17.0.4:7005 172.17.0.4:7006 
    --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 172.17.0.3:7004 to 172.17.0.2:7001
Adding replica 172.17.0.4:7006 to 172.17.0.3:7003
Adding replica 172.17.0.2:7002 to 172.17.0.4:7005
M: 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646 172.17.0.2:7001
   slots:[0-5460] (5461 slots) master
S: 9b0751a4b6ea227658ed26576d9bda1f3b01fc04 172.17.0.2:7002
   replicates 26776d9aef1c511d77efee79a45a63eff18fb4f9
M: 4e2f6532200991c93bbf23f7d4996fbd2aff02ac 172.17.0.3:7003
   slots:[5461-10922] (5462 slots) master
S: 9a627cad72e4192802237c00e4e570b37241feda 172.17.0.3:7004
   replicates 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646
M: 26776d9aef1c511d77efee79a45a63eff18fb4f9 172.17.0.4:7005
   slots:[10923-16383] (5461 slots) master
S: fc25b50d6afe39211a47609287afd250c9e3183b 172.17.0.4:7006
   replicates 4e2f6532200991c93bbf23f7d4996fbd2aff02ac
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 172.17.0.2:7001)
M: 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646 172.17.0.2:7001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 26776d9aef1c511d77efee79a45a63eff18fb4f9 172.17.0.4:7005
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 9a627cad72e4192802237c00e4e570b37241feda 172.17.0.3:7004
   slots: (0 slots) slave
   replicates 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646
S: 9b0751a4b6ea227658ed26576d9bda1f3b01fc04 172.17.0.2:7002
   slots: (0 slots) slave
   replicates 26776d9aef1c511d77efee79a45a63eff18fb4f9
S: fc25b50d6afe39211a47609287afd250c9e3183b 172.17.0.4:7006
   slots: (0 slots) slave
   replicates 4e2f6532200991c93bbf23f7d4996fbd2aff02ac
M: 4e2f6532200991c93bbf23f7d4996fbd2aff02ac 172.17.0.3:7003
   slots:[5461-10922] (5462 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.
复制代码

创建完成后,可以在其它实例上用 redis-cli --cluster check 检查集群状态:

[root@centos-02 init.d]# redis-cli --cluster check 172.17.0.3:7003
172.17.0.3:7003 (4e2f6532...) -> 0 keys | 5462 slots | 1 slaves.
172.17.0.4:7005 (26776d9a...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.2:7001 (9b4f3c2c...) -> 0 keys | 5461 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 172.17.0.3:7003)
M: 4e2f6532200991c93bbf23f7d4996fbd2aff02ac 172.17.0.3:7003
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
M: 26776d9aef1c511d77efee79a45a63eff18fb4f9 172.17.0.4:7005
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: fc25b50d6afe39211a47609287afd250c9e3183b 172.17.0.4:7006
   slots: (0 slots) slave
   replicates 4e2f6532200991c93bbf23f7d4996fbd2aff02ac
S: 9a627cad72e4192802237c00e4e570b37241feda 172.17.0.3:7004
   slots: (0 slots) slave
   replicates 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646
S: 9b0751a4b6ea227658ed26576d9bda1f3b01fc04 172.17.0.2:7002
   slots: (0 slots) slave
   replicates 26776d9aef1c511d77efee79a45a63eff18fb4f9
M: 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646 172.17.0.2:7001
   slots:[0-5460] (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.
复制代码

至此,一个三台服务器 三主三从 的Redis集群就搭建好了。

集群原理

数据分布算法

切片集群是一种保存大量数据的通用机制,这个机制可以有不同的实现方案,Redis 官方提供的集群化方案为 Redis Cluster,Redis Cluster 方案中规定了数据和实例的对应规则。

哈希槽

Redis Cluster 采用哈希槽(Hash Slot)来处理数据和实例之间的映射关系,Redis Cluster 将集群划分为 16384 个槽,每个键值对都会根据它的 key,被映射到一个哈希槽中。

在创建集群时,redis-cli --cluster create 命令会自动把 16384 个槽平均分布在集群实例上,如果集群有 N 个Master,那每个Master上槽的个数为 16384 / N

通过前面创建集群的日志可以得知,每个Master对应了哪些 Slot,以及 Master -> Slave 的关系。

Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.17.0.3:7004 to 172.17.0.2:7001
Adding replica 172.17.0.4:7006 to 172.17.0.3:7003
Adding replica 172.17.0.2:7002 to 172.17.0.4:7005
M: 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646 172.17.0.2:7001
   slots:[0-5460] (5461 slots) master
S: 9b0751a4b6ea227658ed26576d9bda1f3b01fc04 172.17.0.2:7002
   replicates 26776d9aef1c511d77efee79a45a63eff18fb4f9
M: 4e2f6532200991c93bbf23f7d4996fbd2aff02ac 172.17.0.3:7003
   slots:[5461-10922] (5462 slots) master
S: 9a627cad72e4192802237c00e4e570b37241feda 172.17.0.3:7004
   replicates 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646
M: 26776d9aef1c511d77efee79a45a63eff18fb4f9 172.17.0.4:7005
   slots:[10923-16383] (5461 slots) master
S: fc25b50d6afe39211a47609287afd250c9e3183b 172.17.0.4:7006
   replicates 4e2f6532200991c93bbf23f7d4996fbd2aff02ac
复制代码

当然,如果某台机器内存、CPU等配置较高,我们可以给这台机器的Redis实例分配更多的槽。重新分配槽的命令为 redis-cli --cluster reshard,例如下面将 172.17.0.2:7001(..2f5ca7bf646) 上500个 Slots 移到 172.17.0.2:7003(..fbd2aff02ac) 去。

[root@centos-01 /]# redis-cli --cluster reshard 172.17.0.2:7001 
    --cluster-from 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646 
    --cluster-to 4e2f6532200991c93bbf23f7d4996fbd2aff02ac 
    --cluster-slots 500
复制代码

重新分配后,可以再用 redis-cli --cluster check 命令检查槽位配置。

槽位定位算法

Redis Cluster 默认会对 key 值使用 CRC16 算法计算得到一个 16 bit 的hash整数,然后用这个整数值对 16384 取模,得到一个 0~16383 范围内的模数,每个模数就代表一个相应编号的哈希槽,就得到了具体的槽位。

Redis Cluster 还允许用户强制某个 key 挂在特定槽位上,通过在 key 字符串里面嵌入 {tag} 标记,CRC16 就会只计算 tag 的 hash 值。例如:user_{system}、role_{system},这两个 key 都只对 system 计算 hash 值,那最终这两个 key 都会落到同一个槽上。

哈希槽的优点

Redis Cluster 不是直接维护key与实例的映射关系,而是维护了哈希槽和实例的映射关系,这就需要客户端先用 CRC16 计算key的hash值,那为什么不直接维护key与实例的映射关系呢?

  • 首先,整个集群存储key的数量是无法预估的,直接存储key与实例的映射关系,这个映射表可能会非常庞大,这个映射表无论是存储在服务端还是客户端都会占用非常大的内存空间。

  • Redis Cluster 采用去中心化的模式,每个实例会与其它实例交换自己的槽位配置,存储了整个集群完整的映射关系,如果是存储key与实例的关系,节点之间交换信息会变得庞大,消耗过多的网络资源。

  • 当集群在扩容、缩容、数据均衡时,节点之间会发生数据迁移,迁移时需要修改每个key的映射关系,维护成本高。

通过在中间增加一层哈希槽,可以把数据和节点解耦,key通过hash计算,只需要关心映射到了哪个哈希槽,然后再通过哈希槽和节点的映射表找到节点,相当于消耗了很少的CPU资源,不但让数据分布更均匀,还可以让这个映射表变得很小,利于客户端和服务端保存,节点之间交换信息时也变得轻量。在数据迁移时,都以哈希槽为基本单位进行操作,也简化了节点扩容、缩容的难度,便于集群的维护和管理。

槽位配置信息

Redis 集群中的客户端(jedis、lettuce等)连接到集群实例时,它会得到一份集群的槽位配置信息,然后客户端会把哈希槽信息缓存在本地。当客户端请求键值对时,会先计算键所对应的哈希槽,然后就可以给哈希槽对应的实例发送请求了。

那每个实例怎么知道其它实例负责的槽位呢?这是因为集群中的实例相互连接时,会将自己的哈希槽信息发送给对方,当实例之间相互连接后,每个实例就有所有哈希槽的映射关系了。这样客户端在连接到某个实例时,就可以得到集群的整个哈希槽映射关系了。

集群的槽位信息存储于每个节点中,它不需要另外的分布式存储来存储节点槽位信息。前面在创建集群时,我们通过 cluster-config-file 指定了集群配置文件,集群的每个节点会将集群的哈希槽信息持久化到这个配置文件中,所以必须确保配置文件是可写的,而且不要人工修改配置文件。有了这个文件,实例宕机重启也能恢复槽位信息配置。

例如下面的两个配置文件: image.png

槽位重定向

前面说了,Redis集群是去中心化的模式,集群中每个实例都有一份整个集群完整的槽位映射表,这就为客户端提供了纠错能力,因为客户端访问的槽位可能并不在目标实例上。

Redis集群的客户端连接集群时,它也会得到一份集群的槽位配置信息,然后缓存在本地。但如果集群槽位信息发生变更,比如扩容、缩容,节点之间可以通过相互传递消息来获得最新的哈希槽分配信息,但是客户端是无法主动感知这些变化的。

Redis Cluster 提供了一种重定向机制,在客户端给一个实例发送指令时,该实例发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端返回一个特殊的跳转指令 MOVED,其中就包含了这个 key 所在的槽位和实例,然后客户端再重新去访问这个新实例。

例如下面在 7001 上访问返回了 MOVED 命令,说明了请求的键值对所在的哈希槽为 5798,实际是在 172.17.0.3:7003 这个实例上。这时客户端会更新本地缓存的槽位信息,然后向新实例重新发送指令。

image.png

数据迁移

在集群中,我们可能会进行扩容、缩容,或是负载均衡等操作,Redis Cluster 就会把哈希槽重新分布一遍,然后就会迁移数据。

实例之间进行数据迁移都是以哈希槽为基本单位进行操作,当一个槽正在迁移时,这个槽就处于中间过渡状态。那么就有可能这个槽的一部分数据已迁移到目标节点,一部分数据还在原节点。

客户端在访问原节点时,如果对应的数据还在原节点里,那么原节点正常处理。如果对应的数据不在原节点里,那么有两种可能,要么该数据在新节点里,要么根本就不存在。旧节点不知道是哪种情况,所以它会向客户端返回一个 ASK 的重定向指令,返回的 ASK 指令告诉了客户端这个槽位的数据正在迁移中,并且告诉客户端这个槽位的新实例地址。客户端收到这个重定向指令后,先去目标节点执行一个不带任何参数的 ASKING 指令,然后在目标节点再重新执行原先的操作指令。

GET hello:key
(error) ASK 13320 172.17.0.3:7003
复制代码

之所以要先发送 ASKING 指令,是因为在迁移没有完成之前,这个槽位不归新节点管理,如果这个时候向新节点发送该槽位的指令,它会向客户端返回一个 MOVED 重定向指令告诉它去原节点去执行,这样就会导致 循环重定向。而 ASKING 指令就是告诉新节点强制执行下一条指令,把下一条指令当成自己的槽位来处理。

MOVED 命令不同,ASK 命令并不会更新客户端缓存的哈希槽映射信息。所以,如果客户端再次请求这个槽中的数据,它还是会给原节点发送请求。

另外,Redis Cluster 的数据迁移是同步的,迁移一个key会同时阻塞原节点和目标节点,迁移过程中会有一定的性能问题。

主备切换

Redis 集群中的 Slave 节点主要是用于做数据的热备、主备切换,实现集群的高可用的,主库宕机后,集群就会从主库的 Slave 节点中选举出一个节点切换为新主库。

主备切换选举的过程与哨兵模式的原理非常类似,可参考:Redis系列(3) — 哨兵高可用,下面简单说明下集群模式中主备切换的原理。

集群中的 Master 节点之间会不断发送 PING 消息来交换信息,并确认节点间的通信是否正常等。

如果一个节点 PING 另一个节点,在 cluster-node-timeout 内都没有返回,那么就认为另一个节点宕机,这个节点的状态就是主观下线(sdown),如果超过半数节点都认为另一个节点宕机了,那就是客观下线(odown)

判断节点客观下线后,就要从 节点的Slave节点中重选一个,这时会按如下规则进行选举:

  • Slave 节点与 Master 节点断开连接的时间如果超过了 cluster-node-timeout * cluster-replica-validity-factor,就会被剔除
  • 根据Slave节点对Master节点复制数据的 offset 排序,offset 越大,优先选举
  • 如果 offset 一样,所有 Master 节点对这些 Slave 节点投票,得票超过半数(N/2+1)的就选举通过。
cluster-node-timeout 15000
cluster-replica-validity-factor 10
复制代码

选举出新的节点后,就执行主备切换的过程。整个流程跟哨兵非常类似,可以看出 Redis Cluster 功能更强大,集成了 主从复制 和 哨兵 的功能。

集群实验

主从切换

下面通过实验来验证下主库宕机后,从库是否会自动切换为主库。

通过前面的部署可以知道集群节点的关系如下,以 7003 为例做测试:

172.17.0.2:7001(Master) -> 172.17.0.3:7004(Slave)
172.17.0.4:7005(Master) -> 172.17.0.2:7002(Slave)
172.17.0.3:7003(Master) -> 172.17.0.4:7006(Slave)
复制代码

先在 7003(Master) 上验证操作可行:

image.png

在 对应的 7006(Slave)上不可操作:

image.png

此时将 7003 kill 掉:

image.png

这时再检查集群状态,会发现之前的 7006(Slave)已经切换为 Master 了:

[root@centos-01 ~]# redis-cli --cluster check 172.17.0.2:7001
Could not connect to Redis at 172.17.0.3:7003: Connection refused
172.17.0.2:7001 (9b4f3c2c...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.4:7005 (26776d9a...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.4:7006 (fc25b50d...) -> 1 keys | 5462 slots | 0 slaves.
[OK] 1 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 172.17.0.2:7001)
M: 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646 172.17.0.2:7001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 26776d9aef1c511d77efee79a45a63eff18fb4f9 172.17.0.4:7005
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 9a627cad72e4192802237c00e4e570b37241feda 172.17.0.3:7004
   slots: (0 slots) slave
   replicates 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646
S: 9b0751a4b6ea227658ed26576d9bda1f3b01fc04 172.17.0.2:7002
   slots: (0 slots) slave
   replicates 26776d9aef1c511d77efee79a45a63eff18fb4f9
M: fc25b50d6afe39211a47609287afd250c9e3183b 172.17.0.4:7006
   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.
复制代码

再连接到 7006 上,这时发现可以进行操作了:

image.png

接下来再把之前的 7003 重新启动:

image.png

再检查集群的状态,可以看到 7003(...2aff02ac) 自动变为了 7006(...50c9e3183b) 的从库。

[root@centos-01 ~]# redis-cli --cluster check 172.17.0.2:7001
172.17.0.2:7001 (9b4f3c2c...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.4:7005 (26776d9a...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.4:7006 (fc25b50d...) -> 1 keys | 5462 slots | 1 slaves.
[OK] 1 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 172.17.0.2:7001)
M: 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646 172.17.0.2:7001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 26776d9aef1c511d77efee79a45a63eff18fb4f9 172.17.0.4:7005
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 9a627cad72e4192802237c00e4e570b37241feda 172.17.0.3:7004
   slots: (0 slots) slave
   replicates 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646
S: 9b0751a4b6ea227658ed26576d9bda1f3b01fc04 172.17.0.2:7002
   slots: (0 slots) slave
   replicates 26776d9aef1c511d77efee79a45a63eff18fb4f9
M: fc25b50d6afe39211a47609287afd250c9e3183b 172.17.0.4:7006
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 4e2f6532200991c93bbf23f7d4996fbd2aff02ac 172.17.0.3:7003
   slots: (0 slots) slave
   replicates fc25b50d6afe39211a47609287afd250c9e3183b
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
复制代码

通过上面的测试说明主库宕机时,从库会自动切换为主库顶上去,原主库重启后,会自动变为新主库的从库。

集群扩容

如果要提高集群的读写吞吐量、或支撑更多的数据,这时就需要做集群扩容,增加节点,下面就来实验一下。

增加一台 172.17.0.5 的服务器,同样的,新增 一主一从 两个节点,端口分别为 7007、7008

先按照前面集群搭建的步骤把基础环境装好:

# 创建目录
# mkdir -p /var/redis/log
# mkdir -p /var/redis/7007
# mkdir -p /var/redis/7008

# 拷贝配置文件,并修改相关配置
# cp /usr/local/src/redis-6.2.5/redis.conf /etc/redis/7007.conf
# cp /usr/local/src/redis-6.2.5/redis.conf /etc/redis/7008.conf

# 拷贝脚本,并修改相关配置
# cp /usr/local/src/redis-6.2.5/utils/redis_init_script /etc/init.d/redis_7007
# cp /usr/local/src/redis-6.2.5/utils/redis_init_script /etc/init.d/redis_7008
复制代码

将两个节点启动起来:

image.png

集群中增加节点的命令格式如下:

redis-cli --cluster add-node new_host:new_port existing_host:existing_port
    --cluster-slave --cluster-master-id <arg>
复制代码

下面将 7007 添加为集群的 Master:

[root@centos-01 ~]# redis-cli --cluster add-node 172.17.0.5:7007 172.17.0.2:7001
>>> Adding node 172.17.0.5:7007 to cluster 172.17.0.2:7001
......
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 172.17.0.5:7007 to make it join the cluster.
[OK] New node added correctly.
复制代码

此时检查集群状态,发现 7007 没有 slave(0 slaves),也没有分配槽位(0 slots),7007 的实例ID 为 bca31339ca8d8177e9d80831d72b64bc52fdbb87

[root@centos-01 ~]# redis-cli --cluster check 172.17.0.2:7001
172.17.0.2:7001 (9b4f3c2c...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.4:7005 (26776d9a...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.5:7007 (bca31339...) -> 0 keys | 0 slots | 0 slaves.
172.17.0.4:7006 (fc25b50d...) -> 1 keys | 5462 slots | 1 slaves.
[OK] 1 keys in 4 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 172.17.0.2:7001)
M: 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646 172.17.0.2:7001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 26776d9aef1c511d77efee79a45a63eff18fb4f9 172.17.0.4:7005
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
M: bca31339ca8d8177e9d80831d72b64bc52fdbb87 172.17.0.5:7007
   slots: (0 slots) master
S: 9a627cad72e4192802237c00e4e570b37241feda 172.17.0.3:7004
   slots: (0 slots) slave
   replicates 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646
S: 9b0751a4b6ea227658ed26576d9bda1f3b01fc04 172.17.0.2:7002
   slots: (0 slots) slave
   replicates 26776d9aef1c511d77efee79a45a63eff18fb4f9
M: fc25b50d6afe39211a47609287afd250c9e3183b 172.17.0.4:7006
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 4e2f6532200991c93bbf23f7d4996fbd2aff02ac 172.17.0.3:7003
   slots: (0 slots) slave
   replicates fc25b50d6afe39211a47609287afd250c9e3183b
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
复制代码

接下来将 7008 添加为 7007 的 Slave:

[root@centos-01 ~]# redis-cli --cluster add-node 172.17.0.5:7008 172.17.0.2:7001 
        --cluster-slave --cluster-master-id bca31339ca8d8177e9d80831d72b64bc52fdbb87
>>> Adding node 172.17.0.5:7008 to cluster 172.17.0.2:7001
>>> Performing Cluster Check (using node 172.17.0.2:7001)
......
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 172.17.0.5:7008 to make it join the cluster.
Waiting for the cluster to join

>>> Configure node as replica of 172.17.0.5:7007.
[OK] New node added correctly.
复制代码

此时再检查集群状态,可以看到 7008 已经变为 7007 的从库了:

[root@centos-01 ~]# redis-cli --cluster check 172.17.0.2:7001
172.17.0.2:7001 (9b4f3c2c...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.4:7005 (26776d9a...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.5:7007 (bca31339...) -> 0 keys | 0 slots | 1 slaves.
172.17.0.4:7006 (fc25b50d...) -> 1 keys | 5462 slots | 1 slaves.
[OK] 1 keys in 4 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 172.17.0.2:7001)
M: 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646 172.17.0.2:7001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 26776d9aef1c511d77efee79a45a63eff18fb4f9 172.17.0.4:7005
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
M: bca31339ca8d8177e9d80831d72b64bc52fdbb87 172.17.0.5:7007
   slots: (0 slots) master
   1 additional replica(s)
S: 5bfc64bbce07a872d6d6048c62e80f7a8e56990a 172.17.0.5:7008
   slots: (0 slots) slave
   replicates bca31339ca8d8177e9d80831d72b64bc52fdbb87
S: 9a627cad72e4192802237c00e4e570b37241feda 172.17.0.3:7004
   slots: (0 slots) slave
   replicates 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646
S: 9b0751a4b6ea227658ed26576d9bda1f3b01fc04 172.17.0.2:7002
   slots: (0 slots) slave
   replicates 26776d9aef1c511d77efee79a45a63eff18fb4f9
M: fc25b50d6afe39211a47609287afd250c9e3183b 172.17.0.4:7006
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 4e2f6532200991c93bbf23f7d4996fbd2aff02ac 172.17.0.3:7003
   slots: (0 slots) slave
   replicates fc25b50d6afe39211a47609287afd250c9e3183b
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
复制代码

但此时新加入的节点还没有分配哈希槽,我们可以使用 reshardrebalance 命令分配一些槽位到新节点上。例如从 7001(...2f5ca7bf646) 上移动2000个哈希槽到 7007(...c52fdbb87) 上:

[root@centos-01 ~]# redis-cli --cluster reshard 172.17.0.2:7001 
        --cluster-from 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646 
        --cluster-to bca31339ca8d8177e9d80831d72b64bc52fdbb87 
        --cluster-slots 2000
复制代码

再检查集群状态,发现 7007 上已经有哈希槽了:

[root@centos-01 ~]# redis-cli --cluster check 172.17.0.2:7001
172.17.0.2:7001 (9b4f3c2c...) -> 0 keys | 3461 slots | 1 slaves.
172.17.0.4:7005 (26776d9a...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.5:7007 (bca31339...) -> 0 keys | 2000 slots | 1 slaves.
172.17.0.4:7006 (fc25b50d...) -> 1 keys | 5462 slots | 1 slaves.
[OK] 1 keys in 4 masters.
...
[OK] All 16384 slots covered.
复制代码

集群缩容

下面演示删除 172.17.0.5:7007(bca31339ca8d8177e9d80831d72b64bc52fdbb87) 节点。

要删除集群中某个节点,需先将集群上所有的哈希槽移到其它节点后才可以删除,否则会报错:

image.png

先用 reshard 命令将 7007(...c52fdbb87) 节点的哈希槽移动到 7001(...2f5ca7bf646):

[root@centos-01 ~]# redis-cli --cluster reshard 172.17.0.2:7001 
        --cluster-from bca31339ca8d8177e9d80831d72b64bc52fdbb87
        --cluster-to 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646 
        --cluster-slots 2000
复制代码

此时再删除 7007 节点就可以成功执行了:

[root@centos-01 ~]# redis-cli --cluster del-node 172.17.0.2:7002 bca31339ca8d8177e9d80831d72b64bc52fdbb87
>>> Removing node bca31339ca8d8177e9d80831d72b64bc52fdbb87 from cluster 172.17.0.2:7002
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
复制代码

而原本的从节点 7008 就会自动挂载到其它 Master 上去,可以看到 7008 成为 7001 的一个 Slave:

[root@centos-01 ~]# redis-cli --cluster check 172.17.0.2:7001
172.17.0.2:7001 (9b4f3c2c...) -> 0 keys | 5461 slots | 2 slaves.
172.17.0.4:7005 (26776d9a...) -> 0 keys | 5461 slots | 1 slaves.
172.17.0.4:7006 (fc25b50d...) -> 1 keys | 5462 slots | 1 slaves.
[OK] 1 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 172.17.0.2:7001)
M: 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646 172.17.0.2:7001
   slots:[0-5460] (5461 slots) master
   2 additional replica(s)
M: 26776d9aef1c511d77efee79a45a63eff18fb4f9 172.17.0.4:7005
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 5bfc64bbce07a872d6d6048c62e80f7a8e56990a 172.17.0.5:7008
   slots: (0 slots) slave
   replicates 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646
S: 9a627cad72e4192802237c00e4e570b37241feda 172.17.0.3:7004
   slots: (0 slots) slave
   replicates 9b4f3c2ca93b576776e3d3ea5b0c12f5ca7bf646
S: 9b0751a4b6ea227658ed26576d9bda1f3b01fc04 172.17.0.2:7002
   slots: (0 slots) slave
   replicates 26776d9aef1c511d77efee79a45a63eff18fb4f9
M: fc25b50d6afe39211a47609287afd250c9e3183b 172.17.0.4:7006
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 4e2f6532200991c93bbf23f7d4996fbd2aff02ac 172.17.0.3:7003
   slots: (0 slots) slave
   replicates fc25b50d6afe39211a47609287afd250c9e3183b
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
复制代码

当然我们也可以删除 7008 节点:

[root@centos-01 ~]# redis-cli --cluster del-node 172.17.0.2:7002 5bfc64bbce07a872d6d6048c62e80f7a8e56990a
>>> Removing node 5bfc64bbce07a872d6d6048c62e80f7a8e56990a from cluster 172.17.0.2:7002
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
复制代码

猜你喜欢

转载自juejin.im/post/7084163543108927502
今日推荐