浅析 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. 创建目录
bash 复制代码
cd /root/redis-cluster/

mkdir 7001 7002 7003 8001 8002 8003

ll
  1. 在当前目录下创建 redis.conf
bash 复制代码
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
  1. 每个节点都需要配置 redis.conf,以下命令把 redis.conf 批量复制到对应目录。
bash 复制代码
echo 7001 7002 7003 8001 8002 8003 | xargs -t -n 1 cp redis.conf
  1. 批量修改,把6379替换成对应的端口。
bash 复制代码
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t sed -i 's/6379/{}/g' {}/redis.conf
  1. 然后批量启动 Cluster 集群。也可以使用 通过 redis-server + redis.conf文件路径分别启动。
bash 复制代码
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-server {}/redis.conf

查看 redis 相关进程。

bash 复制代码
ps -ef | grep redis
  1. 批量关闭,关闭 Cluster 集群。
bash 复制代码
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-cli -p {} shutdown

虽然服务启动了,但是目前每个服务之间都是独立的,没有任何关联。

所以还需要下面的命令:

bash 复制代码
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. 同样的,创建目录,复制修改配置文件。
bash 复制代码
mkdir 7004
cp redis.conf 7004
sed -i s/6379/7004/g 7004/redis.conf
  1. 启动 redis 服务。
bash 复制代码
redis-server 7004/redis.conf
  1. 把 7004 添加到 Cluster 集群。
bash 复制代码
redis-cli --cluster add-node 192.168.183.128:7004 192.168.183.128:7001
  1. 查看 Cluster 集群信息。
bash 复制代码
redis-cli -c -h 192.168.183.128 -p 7001 cluster nodes

可以看到,目前 7004 还没有分配哈希槽(hash slot)。

  1. 给 7004 结点分配插槽
bash 复制代码
redis-cli --cluster reshard 192.168.183.128:7001
  1. 查看 Cluster 集群信息。
bash 复制代码
redis-cli -p 7001 cluster nodes

分配成功!

  1. 测试,num 已经落到了 7004 节点上。
  1. 删除 7004 节点,先 reshard hash slot。
  1. 再次查看节点信息,发现 7004 没有 hash slot 了,且已经成为了 7001 的 slave。
  1. forget 删除 7004 节点,后面的 id 写 7004 的 id 就行。
bash 复制代码
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

主从读写分离配置还是一样:

java 复制代码
@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 全自动完成,底层已经封装好了,我们无需关注。

相关推荐
剑客狼心24 分钟前
Oracle:什么是存储过程
数据库·oracle
小王努力学编程26 分钟前
【MySQL篇】表的操作
数据库·mysql
汤汤upup28 分钟前
面试八股文--数据库基础知识总结(2) MySQL
数据库·mysql·面试
八股文领域大手子2 小时前
JVM垃圾回收器深度底层原理分析与知识体系构建
java·jvm·算法
虾球xz3 小时前
游戏引擎学习第124天
java·学习·游戏引擎
m0_748250933 小时前
mysql数据被误删的恢复方案
数据库·mysql
m0_748238423 小时前
MySQL Workbench菜单汉化为中文
android·数据库·mysql
码熔burning4 小时前
(十)趣学设计模式 之 外观模式!
java·设计模式·外观模式
一小路一4 小时前
从0-1学习Mysql第五章: 索引与优化
数据库·后端·学习·mysql·面试
计算机毕设定制辅导-无忧学长4 小时前
Maven 生命周期与构建命令(一)
java·maven