【Redis】Redis 分片集群搭建与故障转移实战指南

1 搭建分片集群

主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:

  • 海量数据存储问题

  • 高并发写的问题

使用分片集群可以解决上述问题,如图:

分片集群特征:

  • 集群中有多个 master,每个 master 保存不同数据

  • 每个 master 都可以有多个 slave 节点

  • master 之间通过 ping 监测彼此健康状态

  • 客户端请求可以访问集群任意节点,最终都会被转发到正确节点

1.1 集群结构

分片集群需要的节点数量较多,这里我们搭建一个最小的分片集群,包含 3 个 master 节点,每个 master 包含一个 slave 节点。

这里我们会在同一台虚拟机中开启 6 个 redis 实例,模拟分片集群,信息如下:

IP PORT 角色
172.31.76.134 7001 master
172.31.76.134 7002 master
172.31.76.134 7003 master
172.31.76.134 8001 slave
172.31.76.134 8002 slave
172.31.76.134 8003 slave

1.2 准备实例和配置

这里不在 /tmp 目录下也可以,建议在 home/用户名/ 目录下创建一个新的目录代替 /tmp ,例如我是在 home/horizon/ 目录下新建了 redis-cluster-test 目录,所以我接下来的操作都是在 home/horizon/redis-cluster-test 中进行的,在下面涉及到 /tmp 的配置或操作都需要先修改为 /redis-cluster-test 再操作。

删除之前的 7001、7002、7003 这几个目录,重新创建出 7001、7002、7003、8001、8002、8003 目录:

bash 复制代码
# 进入/redis-cluster-test目录
cd /redis-cluster-test
# 删除旧的,避免配置干扰
rm -rf 7001 7002 7003
# 创建目录
mkdir 7001 7002 7003 8001 8002 8003

在 /redis-cluster-test 下准备一个新的 redis.conf 文件,内容如下:

bash 复制代码
port 6379
# 开启集群功能
cluster-enabled yes
# 集群的配置文件名称,不需要我们创建,由redis自己维护
cluster-config-file /redis-cluster-test/6379/nodes.conf
# 节点心跳失败的超时时间
cluster-node-timeout 5000
# 持久化文件存放目录
dir /redis-cluster-test/6379
# 绑定地址
bind 0.0.0.0
# 让redis后台运行
daemonize yes
# 注册的实例ip
replica-announce-ip 192.168.150.101
# 保护模式
protected-mode no
# 数据库数量
databases 1
# 日志
logfile /redis-cluster-test/6379/run.log

将这个文件拷贝到每个目录下:

bash 复制代码
# 进入/tmp目录
cd /tmp
# 执行拷贝
echo 7001 7002 7003 8001 8002 8003 | xargs -t -n 1 cp redis.conf

修改每个目录下的 redis.conf,将其中的 6379 修改为与所在目录一致:

bash 复制代码
# 进入/tmp目录
cd /tmp
# 修改配置文件
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t sed -i 's/6379/{}/g' {}/redis.conf

1.3 启动

因为已经配置了后台启动模式,所以可以直接启动服务:

bash 复制代码
# 进入/tmp目录
cd /tmp
# 一键启动所有服务
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-server {}/redis.conf

通过 ps 查看状态:

bash 复制代码
ps -ef | grep redis

发现服务都已经正常启动:

如果要关闭所有进程,可以执行命令:

bash 复制代码
ps -ef | grep redis | awk '{print $2}' | xargs kill

或者(推荐这种方式):

bash 复制代码
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-cli -p {} shutdown

1.4 创建集群

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

我们需要执行命令来创建集群,在 Redis5.0 之前创建集群比较麻烦,5.0 之后集群管理命令都集成到了 redis-cli 中。

我们使用的是 5.0 之后的版本,集群管理以及集成到了 redis-cli 中,格式如下:

bash 复制代码
redis-cli --cluster create --cluster-replicas 1 192.168.150.101:7001 192.168.150.101:7002 192.168.150.101:7003 192.168.150.101:8001 192.168.150.101:8002 192.168.150.101:8003

命令说明:

  • redis-cli --cluster 或者 ./redis-trib.rb :代表集群操作命令

  • create :代表是创建集群

  • --replicas 1 或者 --cluster-replicas 1 :指定集群中每个 master 的副本个数为 1,此时"节点总数 ÷ (replicas + 1) "得到的就是 master 的数量。因此节点列表中的前 n 个就是 master,其它节点都是 slave 节点,随机分配到不同 master

运行后的样子:

这里输入 yes,则集群开始创建:

通过命令可以查看集群状态:

bash 复制代码
redis-cli -p 7001 cluster nodes

1.5 测试

尝试连接 7001 节点,存储一个数据:

bash 复制代码
# 连接
redis-cli -c -p 7001
# 存储数据
set num 123
# 读取数据
get num
# 再次存储
set a 1

2 散列插槽

2.1 插槽原理

Redis 会把每一个 master 节点映射到 0~16383 共 16384 个插槽(hash slot)上,查看集群信息时就能看到:

数据 key 不是与节点绑定,而是与插槽绑定。redis 会根据 key 的有效部分计算插槽值,分两种情况:

  • key 中包含"{}",且"{}"中至少包含 1 个字符,"{}" 中的部分是有效部分
  • key 中不包含"{}",整个 key 都是有效部分

例如:key 是 num,那么就根据 num 计算,如果 key 是 {itcast}num,则根据 itcast 计算。计算方式是利用 CRC16 算法得到一个 hash 值,然后对 16384 取余,得到的结果就是 slot 值。

如图,在 7001 这个节点执行 set a 1 时,对 a 做 hash 运算,对 16384 取余,得到的结果是 15495,因此要存储到 7003 节点。

到了 7003 后,执行 get num 时,对 num 做 hash 运算,对 16384 取余,得到的结果是 2765,因此需要切换到 7001 节点。

Redis 如何判断某个 key 应该在哪个实例?

  • 将 16384 个插槽分配到不同的实例
  • 根据 key 的有效部分计算哈希值,对 16384 取余
  • 余数作为插槽,寻找插槽所在实例即可

如何将同一类数据固定的保存在同一个 Redis 实例?

  • 这一类数据使用相同的有效部分,例如 key 都以 {typeId} 为前缀

3 集群伸缩

redis-cli --cluster 提供了很多操作集群的命令,可以通过下面方式查看:

比如,添加节点的命令:

3.1 需求分析

需求:向集群中添加一个新的 master 节点,并向其中存储 num = 10

  • 启动一个新的 redis 实例,端口为 7004
  • 添加 7004 到之前的集群,并作为一个 master 节点
  • 给 7004 节点分配插槽,使得 num 这个 key 可以存储到 7004 实例

这里需要两个新的功能:

  • 添加一个节点到集群中
  • 将部分插槽分配到新插槽

3.2 创建新的 redis 实例

创建一个文件夹:

bash 复制代码
mkdir 7004

拷贝配置文件:

bash 复制代码
cp redis.conf /7004

修改配置文件:

bash 复制代码
sed /s/6379/7004/g 7004/redis.conf

启动:

bash 复制代码
redis-server 7004/redis.conf

3.3 添加新节点到 redis

添加节点的语法如下:

执行命令:

bash 复制代码
redis-cli --cluster add-node 172.31.76.134:7004 172.31.76.134:7001

通过命令查看集群状态:

bash 复制代码
redis-cli -p 7001 cluster nodes

如图,7004 加入了集群,并且默认是一个 master 节点:

但是,可以看到 7004 节点的插槽数量为 0,因此没有任何数据可以存储到 7004 上。

3.4 转移插槽

我们要将 num 存储到 7004 节点,因此需要先看看 num 的插槽是多少:

如上图所示,num 的插槽为 2765。

我们可以将 0~3000 的插槽从 7001 转移到 7004,具体命令如下:

建立连接:

bash 复制代码
redis-cli --cluster reshard 172.31.76.134:7001

得到下面的反馈:

询问要移动多少个插槽,我们计划是3000个:

新的问题来了:

哪个 node 来接收这些插槽??

显然是 7004,那么 7004 节点的 id 是多少呢?

复制这个 id,然后拷贝到刚才的控制台后:

这里询问,你的插槽是从哪里移动过来的?

  • all:代表全部,也就是三个节点各转移一部分
  • 具体的 id:目标节点的 id
  • done:没有了

这里我们要从 7001 获取,因此填写 7001 的 id。

填完后,点击done,这样插槽转移就准备好了。

确认要转移吗?输入 yes。

然后,通过命令查看结果,可以看到:

目的达成。

4 故障转移

集群初始状态是这样的:

其中 7001、7002、7003 都是 master,我们计划让 7002 宕机。

4.1 自动故障转移

当集群中有一个 master 宕机会发生什么呢?

直接停止一个 redis 实例,例如 7002:

bash 复制代码
redis-cli -p 7002 shutdown
  1. 首先是该实例与其它实例失去连接

  2. 然后是疑似宕机

  3. 最后是确定下线,自动提升一个 slave 为新的 master

  4. 当 7002 再次启动,就会变为一个 slave 节点了

4.2 手动故障转移

利用 cluster failover 命令可以手动让集群中的某个 master 宕机,切换到执行 cluster failover 命令的这个 slave 节点,实现无感知的数据迁移。其流程如下:

这种 failover 命令可以指定三种模式:

  • 缺省:默认的流程,如图 1~6 步

  • force:省略了对 offset 的一致性校验

  • takeover:直接执行第 5 步,忽略数据一致性、忽略 master 状态和其它 master 的意见

案例需求:

在 7002 这个 slave 节点执行手动故障转移,重新夺回 master 地位

步骤如下:

  1. 利用 redis-cli 连接 7002 这个节点

  2. 执行 cluster failover 命令

如图:

效果:

5 RedisTemplate访问分片集群

RedisTemplate 底层同样基于 lettuce 实现了分片集群的支持,而使用的步骤与哨兵模式基本一致:

  1. 引入 redis 的 starter 依赖

  2. 配置分片集群地址

  3. 配置读写分离

与哨兵模式相比,其中只有分片集群的配置方式略有差异,如下:

bash 复制代码
spring:
  redis:
    cluster:
      nodes:
        - 172.31.76.134:7001
        - 172.31.76.134:7002
        - 172.31.76.134:7003
        - 172.31.76.134:8001
        - 172.31.76.134:8002
        - 172.31.76.134:8003
相关推荐
咩咩啃树皮7 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
鱟鲥鳚7 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
大模型码小白9 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司9 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地10 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
wuqingshun31415910 小时前
TCP超时重传机制是为了解决什么问题?
java
二宝哥11 小时前
CentOS 7.9 系统下 Redis 5.0.4 单机安装与源码编译安装指南
redis·centos
莫逸风13 小时前
【AgentScope 2.0】 0. 学习指南
java·llm·agent·agentscope
z1234567898613 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
yaoxin52112314 小时前
470. Java 反射 - Member 接口与 AccessFlag
java·开发语言·python