浅析 Redis 分片集群 Cluster 原理、手动搭建、动态伸缩集群、故障转移
大家好,我是此林。
之前的文章中分享了 Redis 集群方案的一种:主从集群+哨兵机制
浅谈 Redis 主从集群原理(一)-CSDN博客
浅谈 Redis 主从复制原理(二)-CSDN博客
这种模式有什么缺点呢?
1. 虽然从节点有多个,提高了读的性能。但主节点只有一个,写的性能无法提高。
2. 要存储大量的数据,就要保证 master 内存容量大。但是 master 内存容量大了以后,fork 生成 RDB 文件的时间就会边长,RDB 文件也会变得很大,导致了下面的问题。
- RDB 文件网络传输开销大
- master 和 slave 第一次做全量同步,耗时长
- 若 master 宕机,新 master 会和 slave 重新全量同步,开销大
所以,要解决上面的问题,实现存储海量的数据,就引入了新的集群方案:分片集群(Cluster)。
1. 分片集群概述
可以发现,分片集群有以下特征:
1. 集群中有多个master,每个master保存不同数据
2. 每个master都可以有多个slave节点
3. master之间通过心跳机制监测彼此健康状态
4. 客户端请求可以访问集群任意节点,最终都会被路由转发到正确节点
可以发现,Cluster 集群里面没有了哨兵机制,而是不同 master 之间通过心跳机制互相检测彼此的健康状态,如果发现有 master 宕机,会和哨兵一样重新选 master ,进行故障转移。
2. 分片集群搭建
这里我们部署6个节点,一主一从。
节点 | IP | 端口 |
master | 192.168.183.128 | 7001 |
master | 192.168.183.128 | 7002 |
master | 192.168.183.128 | 7003 |
slave | 192.168.183.128 | 8001 |
slave | 192.168.183.128 | 8002 |
slave | 192.168.183.128 | 8003 |
1. 创建目录
cd /root/redis-cluster/
mkdir 7001 7002 7003 8001 8002 8003
ll
2. 在当前目录下创建 redis.conf
port 6379
# 开启集群功能
cluster-enabled yes
# 集群的配置文件名称,不需要我们创建,由redis自己维护
cluster-config-file /root/redis-cluster/6379/nodes.conf
# 节点心跳失败的超时时间
cluster-node-timeout 5000
# 持久化文件存放目录
dir /root/redis-cluster/6379
# 绑定地址
bind 0.0.0.0
# 让redis后台运行
daemonize yes
# 注册的实例ip(可以多个)
replica-announce-ip 192.168.183.128
# 保护模式
protected-mode no
# 数据库数量
databases 1
# 日志
logfile /root/redis-cluster/6379/run.log
# requirepass 123123
3. 每个节点都需要配置 redis.conf,以下命令把 redis.conf 批量复制到对应目录。
echo 7001 7002 7003 8001 8002 8003 | xargs -t -n 1 cp redis.conf
4. 批量修改,把6379替换成对应的端口。
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t sed -i 's/6379/{}/g' {}/redis.conf
5. 然后批量启动 Cluster 集群。也可以使用 通过 redis-server + redis.conf文件路径分别启动。
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-server {}/redis.conf
查看 redis 相关进程。
ps -ef | grep redis
6. 批量关闭,关闭 Cluster 集群。
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-cli -p {} shutdown
虽然服务启动了,但是目前每个服务之间都是独立的,没有任何关联。
所以还需要下面的命令:
redis-cli --cluster create \
--cluster-replicas 1 \
192.168.183.128:7001 192.168.183.128:7002 192.168.183.128:7003 \
192.168.183.128:8001 192.168.183.128:8002 192.168.183.128:8003
--cluster-replicas 1 代表每个 master 有1个从节点。
那么列个方程,假设 master 有 x 个,那么 slave 就有 x 个,x + x = 6,x = 3。
那么计算出来,master 3 个,slave 3个。
前三个 IP+端口 就是 master,后 3 个就是 slave。
3. 散列插槽
Cluster 集群启动后,可以发现每一个 master 节点都被映射到了0-16383共16384个插槽上(hash slot)上。
那为什么要有散列插槽呢?
Redis 在存一个 <key, value> 的时候,会通过 CRC16 哈希算法对 key 进行运算,得到一个 hash 值,然后对 16384 取余,得到结果就是 slot 值,以此来 决定这个 <key, value> 落到哪一个 master 上。
注:如果 key 是 num,那就根据 num 计算 hash,如果 key 是 {iphone}num,那么就根据 phone 计算 hash。
也就是说,如果将来我们希望某些 key 落到同一个 master 上,可以在 set key 的时候,加上 {},这样 redis 就会根据花括号里的内容计算 hash 值,从而落到同一个节点上。
重定向举例:
4. 集群动态伸缩
Cluster 集群有个重要的特点,就是集群能够动态增加或删除节点。
案例:
向集群添加一个新的 master 节点,并向其中存储 num = 10
需求:
1. 启动一个新的 redis 实例,端口为 7004
2. 添加 7004 到 Cluster 集群,并且作为一个 master
3. 给 7004 分配插槽,使得 num 这个 key 可以存储到 7004
1. 同样的,创建目录,复制修改配置文件。
mkdir 7004
cp redis.conf 7004
sed -i s/6379/7004/g 7004/redis.conf
2. 启动 redis 服务。
redis-server 7004/redis.conf
3. 把 7004 添加到 Cluster 集群。
redis-cli --cluster add-node 192.168.183.128:7004 192.168.183.128:7001
4. 查看 Cluster 集群信息。
redis-cli -c -h 192.168.183.128 -p 7001 cluster nodes
可以看到,目前 7004 还没有分配哈希槽(hash slot)。
5. 给 7004 结点分配插槽
redis-cli --cluster reshard 192.168.183.128:7001
6. 查看 Cluster 集群信息。
redis-cli -p 7001 cluster nodes
分配成功!
7. 测试,num 已经落到了 7004 节点上。
8. 删除 7004 节点,先 reshard hash slot。
9. 再次查看节点信息,发现 7004 没有 hash slot 了,且已经成为了 7001 的 slave。
10. forget 删除 7004 节点,后面的 id 写 7004 的 id 就行。
redis-cli -c -h 192.168.183.128 -p 7001 cluster forget d8ec7fc70c782114d6db60eb11863ad7a24801b8
5. 故障转移
当一个集群中一个 master 宕机了会发生什么?
-
与其他实例失去连接:当一个 master 节点宕机时,它和其他节点的连接会中断,集群中的节点会检测到该节点不可用。
-
其他 master 监测到该节点宕机(疑似宕机):由于 Redis Cluster 本身是去中心化的,不同于哨兵机制,集群内的其他节点会通过 Gossip 协议(节点之间相互通知)来交换信息,检测到宕机的节点,但这并不会立刻导致故障转移。
-
最后确定下线,选举一个 slave 成为 master:如果集群中的节点确认该 master 节点宕机(通过 QUORUM 达成共识),集群会通过故障转移机制,将该节点的 slave 之一提升为新的 master。在 Redis Cluster 中,只有当 slave 存在且健康时,才会进行主从切换。
注:如果一个 master 节点宕机,但没有足够的 slave 节点(或者没有足够的健康 slave),那么该槽的服务将暂时不可用,直到新的 master 被选举出来。
所以,整体来说,Cluster 的容错机制确实也与哨兵机制类似,但集群是去中心化的,节点之间的自动故障转移是通过 Gossip 协议和共识机制来实现的。
手动故障转移
有时候,比如 7001 机器比较老旧,需要升级。
我们新开一个机器性能好的结点,主动成为 7001 的 slave。
然后通过 cluster failover 命令手动让 7001 宕机。
这时候新开的结点被选举为master,实现机器的无感故障转移升级。
6. RedisTemplate 配置
和哨兵的玩法类似,只不过配置文件稍微有点不同。
application.yml
主从读写分离配置还是一样:
@Bean
public LettuceClientConfigurationBuilderCustomizer configurationBuilderCustomizer() {
return configBuilder -> configBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}
这里的 ReadFrom 是配置 Redis 的读取策略:
MASTER:从主节点读取
MASTER_PERFERRED:优先从 MASTER 读取,MASTER 不可用才读取 slave。
REPLICA:从从节点读取
REPLICA_PERFERRED:优先从从节点读取,slave 不可用才读取 MASTER。
主从切换、读写分离、故障转移通知接收由 RedisTemplate 全自动完成,底层已经封装好了,我们无需关注。