分布式缓存(Redis集群)
前言 单节点Redis的问题
1.数据丢失
Redis基于内存存储,服务器重启可能会导致数据丢失
2.并发能力
单节点Redis的并发能力虽然已经很不错,但是依然无法满足大型的高并发场景
3.故障恢复
如果Redis宕机,则服务将不可用。因此需要一种自动的故障恢复手段
4.存储能力
Redis基于内存,单节点存储的数据量有限,难以满足海量数据的需求
5.解决Redis问题的方案
一 搭建单节点Redis
1.使用安装包搭建单节点
先下载 Redisan安装包,笔者编辑此文档时,最新版为redis-6.0.6.tar.gz
,下载完成之后将安装包放置在虚拟机或服务器的指定位置下。
#1.安装依赖
yum install -y gcc tcl
#2.解压文件
tar -xvf redis-6.0.6.tar.gz
#3.进入redis文件目录
cd /redis-6.0.6
#4.编译安装
make && make install
安装过程中出现以下错误时,可以按照以下方案解决
‘struct redisServer’ has no member named ‘sentinel_mode’
if (server.sentinel_mode && configfile && *configfile == '-')
解决方案:Redis: 部署redis6.0.6 出现问题_天黑请赶路的博客-CSDN博客
处理完成以后,重新运行make && make install
命令即可完成安装
2.Docker搭建单节点 Redis
使用Docker
创建一个Redis容器
并且运行即可
docker run --name myredis \
-v /usr/local/redis:/usr/local/etc/redis \
-p 6379:6379 \
-d redis:latest \
redis-server /usr/local/etc/redis/redis.conf
- -v:挂载自己的redis配置文件目录到容器指定目录
- -p:对外映射访问端口
- redis-server /usr/local/etc/redis/redis.conf:指定要使用的
redis.conf
更多用法参考DockerHub:redis - Official Image | Docker Hub
踩坑点:redis.conf文件需要手动创建
Redis及配置文件下载地址:redis下载 – Redis中国用户组(CRUG)
二 Redis持久化
1.RDB持久化
1.RDB基础
RDB
全称为Redis Database Backup file(Redis数据备份文件),也叫作Redis数据快照
。
简单来说就是把内存中的所有数据都记录到磁盘中,当Redis实例故障后,从磁盘读取快照文件,恢复数据。
2.RDB存储
RDB存储
方式默认由Redis开启
快照文件
称之为RDB文件
,默认保存在当前运行目录
。
通过 save
命令来实现RDB持久化
,该命令由Redis
主进程来执行,会阻塞所有命令。
使用
save
保存数据,当数据过大时,会导致其他线程无法访问Redis,缺点尤为明显
这里推荐使用bgsave
命令,使用后台保存命令。该命令是在后台异步执行的,且由子进程执行RDB,不会造成主进程的阻塞。
注意:Redis停机时会自动执行一次RDB
Redis内部有触发RDB的机制,可以在 redis.conf 文件夹中找到。其中关于RDB的默认配置如下:
# 含义:900s内如果有1个key被修改,则执行 bgsave,其他行同理。
# 如果是 save "" 则表示删除所有的保存点。
# 禁用所有 save 表示完全禁用 RDB
# save ""
save 900 1
save 300 10
save 60 10000
#其他配置信息
#是否压缩
rdbcompression yes
#是否校验和
rdbchecksum yes
#压缩文件名称
dbfilename dump.rdb
#是否删除非持久性实例中的rdb文件
rdb-del-sync-files no
#RDB文件保存位置,默认为当前目录
dir ./
3.RDB原理
bgsave
命令开始执行时,会 fork 主进程得到子进程,子进程共享主进程的内存数据。
完成 fork 后读取内存数据并写入RDB文件
中。
fork 底层采用了 copy-on-write
技术:
- 当主进程执行
读操作
时,访问共享内存 - 当主进程执行
写操作
时,则会拷贝一份数据,执行写操作
整体流程:
- fork主进程得到一个子进程,共享内存空间
- 子进程读取内存数据并且写入RDB文件
- 使用新的RDB文件替换旧的RDB文件
RDB的缺点:
- RDB执行间隔时间长,两次RDB之间写入数据有丢失的风险
- fork子进程,压缩,写入RDB文件都比较耗时
2.AOF持久化
1.AOF基础
AOF
全称为Append Only FIle(追加文件)
。Redis处理的每一个命令都会记录在AOF
中,可以看做是命令日志文件。
2.AOF存储
AOF
默认是关闭状态,需要修改redis.conf
配置文件开开启AOF
:
#是否开启AOF功能,默认为 no
appendonly yes
#AOF文件默认名称
appendfilename "appendonly.aof"
# AOF文件记录频率,Redis提供了三种方案,分别是 always,everysec,no
appendfsync everysec
- everysec:写命令执行完先放入缓冲区,然后每隔1秒将缓冲区的数据写到AOF文件,是默认方案。牺牲一定的可靠性,但性能较好
- no:写命令执行完先放入缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。性能最好,但是安全性最差
- always:每执行一次写命令,立即记录到AOF文件,数据得到绝对的安全保障,但是性能最差
3.存储示例
注意:Redis
的RDB
和AOF
可以共同存在,一起保障数据的安全
修改redis.conf
的关键配置如下:
# 注释掉所有 save 行,开启 AOF 存储
appendonly yes
重启Redis服务
,此时默认目录下会生成 appendonlydir
文件,真对于 redis
的操作记录,全都记录在这个文件夹中。
# 进入 redis 容器内部
docker exec -it myredis bash
# 查看当前目录下的文件
ls
# 利用redis-cli工具操作redis,并且存储一个String数据
set name tony;
其中 /appendonlydir/appendonly.aof.1.incr.aof
中就保存着我们操作redis
的记录。
使用 cat
命令查看文件内容
# 切换至 appendonlydir 目录下
cd /appendonlydir
# 查看文件内容
cat appendonly.aof.1.incr.aof
${num}
表示输入命令的字符串长度,例如 $6
表示输入了一个长度为 6 的信息。
以上文件内容表示执行了以下Redis 命令:
# 选择 0 号库
select 0
# 存储一个key=name,value=tony的字符串数据
set name tony
因为是记录命令,AOF文件
会比RDB文件
大很多。而且对同一个key的多次操作,只有最后一次才有意义。这样一来就存储了很多无用的数据。
通过执行bgrewriteaof
命令,可以让AOF文件
执行重写功能,用最少的命令达到相同的效果。
bgrewriteaof
自动重写功能默认为开启状态,我们可以通过配置文件,设置触发自动重写的条件:
# 相比上次文件大小,超过指定百分比则触发重写
auto-aof-rewrite-percentage 100
# aof文件的大小达到指定大小时开始触发重写
auto-aof-rewrite-min-size 64mb
3.持久化模式对比
实际生产环境下往往采用两者结合
的方式来使用。
三 Redis主从
单节点Redis的并发能力是有上线的,要进一步提高Redis的并发能力,就需要搭建主从架构,实现读写分离。
在一台服务器上开启多个端口,模拟实现搭建Redis主从架构。
1.搭建Redis主从架构
创建3个文件目录,使得它们分别去运行,彼此之间互不干扰
mkdir 7001 7002 7003
准备一份原始的redis配置文件redis.conf,然后将它复制3份,分别放在3个文件夹下面
可以通过 查找,获取到刚才安装的Redis的配置文件,恢复至原始状态,然后将其复制到创建的3个目录下,
find / -name redis.conf
可以使用管道组合命令,一键拷贝
echo 7001 7002 7003 | xargs -t -n 1 cp /usr/local/redis-6.0.6/redis.conf
修改数据保存目录为3个文件夹的各自目录以及端口号,分别执行一下命令,可快捷完成更改
sed -i -e 's/6379/7001/g' \
-e 's/dir .\//dir \/tmp\/7001\//g' \
-e 's/logfile ""/logfile "\/tmp\/7001\/redis.log"/g' \
7001/redis.conf;
sed -i -e 's/6379/7002/g' \
-e 's/dir .\//dir \/tmp\/7002\//g' \
-e 's/logfile ""/logfile "\/tmp\/7002\/redis.log"/g' \
7002/redis.conf;
sed -i -e 's/6379/7003/g' \
-e 's/dir .\//dir \/tmp\/7003\//g' \
-e 's/logfile ""/logfile "\/tmp\/7003\/redis.log"/g' \
7003/redis.conf;
声明每个实例的IP,因为虚拟机本身具有多个IP,为避免混乱和变化,最好为每个实例指定固定的IP地址,可使用一下命令快捷修改
sed -i '1a replica-announce-ip 192.168.119.101' 7001/redis.conf;
sed -i '1a replica-announce-ip 192.168.119.101' 7002/redis.conf;
sed -i '1a replica-announce-ip 192.168.119.101' 7003/redis.conf;
更改redis.conf的bind
和daemonize
属性,允许远程访问和后台运行,可以进行快捷修改
sed -i -e 's/127.0.0.1/0.0.0.0/g' -e 's/daemonize no/daemonize yes/g' 7001/redis.conf;
sed -i -e 's/127.0.0.1/0.0.0.0/g' -e 's/daemonize no/daemonize yes/g' 7002/redis.conf;
sed -i -e 's/127.0.0.1/0.0.0.0/g' -e 's/daemonize no/daemonize yes/g' 7003/redis.conf;
然后分别运行3个redis实例,运行的时候需指定特定的redis.conf
文件
redis-server 7001/redis.conf;
redis-server 7002/redis.conf;
redis-server 7003/redis.conf;
运行完成之后,查看redis进程
ps -ef |grep redis
可以看到3个redis实例已经全部运行成功。
最后一步,配置主从关系,我们以 7001
实例作为master
,7002,7003
作为replica/slave
,然后分别修改redis.conf
配置文件
其中,master主节点不需要修改,只需要修改两个从节点即可
# 格式: replicaof host port
replicaof 192.168.119.101 7001
# 或者
salveof 192.168.119.101 7001
Redis5.0之前使用 slaveof 参数,Redis5.0之后使用 replicaof 参数,两者效果完全一样
使用快捷命令进行修改
sed -i '1a replicaof 192.168.119.101 7001' 7002/redis.conf;
sed -i '1a replicaof 192.168.119.101 7001' 7003/redis.conf;
修改完成之后,关闭3个redis进程,然后全部重启,进行测试即可。
kill 9 57781 57787 57811
至此,Redis集群搭建完成
2.Docker 搭建主从架构
准备3份redis的配置文件(使用默认配置即可),修改配置文件中RDB的存储位置为/data/
。且将配置文件分别存储在不同的目录下:
关键配置信息如下:
# 1.修改 dir 的值为 /data/
dir /data/
# 2.注释掉 bind 127.0.0.1,允许远程访问
#bind 127.0.0.1
# 3.关闭保护模式,修改参数值为 no
protected-mode no
运行以下 docker 命令,创建3个redis容器:
docker run --name redis-master \
-v /usr/local/redis-1:/usr/local/etc/redis \
-v /usr/local/redis-1/data/:/data/ \
-p 7001:6379 \
-p 27001:27001 \
--network bridge \
-d redis:latest \
redis-server /usr/local/etc/redis/redis.conf;
docker run --name redis-replica-1 \
-v /usr/local/redis-2:/usr/local/etc/redis \
-v /usr/local/redis-2/data/:/data/ \
-p 7002:6379 \
-p 27002:27002 \
--network bridge \
-d redis:latest \
redis-server /usr/local/etc/redis/redis.conf;
docker run --name redis-replica-2 \
-v /usr/local/redis-3:/usr/local/etc/redis \
-v /usr/local/redis-3/data/:/data/ \
-p 7003:6379 \
-p 27003:27003 \
--network bridge \
-d redis:latest \
redis-server /usr/local/etc/redis/redis.conf;
-v /usr/local/redis-1/data/:/data/
:将redis容器的RDB文件目录/data/
挂载到Linux服务器目录/usr/local/redis-1/data/
上
创建容器时容器的网络模式使用了桥接模式(bridge)
,这是Docker
中的默认网络模式,创建了一个虚拟网卡docker0
,通过docker0网桥
以及iptables nat
表配置与宿主机通信,此模式会为每一个容器分配Network Namespace
、设置IP等,并将一个主机上的 Docker 容器连接到一个虚拟网桥上。
使用以下命令查看虚拟网卡docker0
的配置信息
iptables -t nat -nvL
使用虚拟网卡
docker0
提供的Network Namespace
以及ip地址
,可以实现容器之间的相互通信。关于Docker
网络模式内容,请查阅Dokcer官网
我们以7001端口的容器
作为主机,由上图可知,映射端口为7001端口的容器
的虚拟网桥地址是172.12.0.2:6379
。
在从机(replace / slave) 的配置文件 redis.conf 中指定主机(master)的IP地址和端口号
# 格式: replicaof host port
replicaof 172.17.0.2 6379
# 或者
salveof 172.17.0.2 6379
Redis5.0之前使用 slaveof 参数,Redis5.0之后使用 replicaof 参数
完成之后重启容器进行测试,此时主机
的日志会给出提示,如下图:
主从架构搭建完成以后,只有主机(master)
可以做写操作
,而从机(slave)
只能做读操作
。
3.主从数据同步原理
1.全量同步
主从第一次同步是全量同步
。
master
如何判断slave
是不是第一次来同步数据?这里会用到两个很重要的概念:
- Replication Id:简称
replid
,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid
,slave则会继承master节点的replid
- offset:偏移量,随着记录在
repl_baklog
中的数据增多而逐渐变大。slave
完成同步时也会记录当前同步的offset
。如果slave
的offset
小于master
的offset
,说明slave
数据落后于master
,需要更新。
因此slave做数据同步,必须向master
声明自己的Replication Id
和offset
,master
才能判断到底需要同步哪些数据。
Redis底层
通过Replication Id
来判断是否为第一次数据,如果master
的Replication Id
和slave
不一样,则说明是第一次同步数据。
全量同步的完整流程:
slave
节点请求增量同步master
节点判断repl_Id
,发现不一致,拒绝增量同步master
将完整内存数据生成RDB,发送RDB到slave
slave
清空本地数据,加载master
的RDBmaster
将RDB期间的命令记录在repl_baklog
,并持续将repl_baklog中的命令发送给slave
slave
执行接收到的命令,保持与master
之间的同步
2.增量同步
主从第一次同步是全量同步
,但如果slave重启后同步,则执行增量同步
repl_baklog有大小上限,写满后会覆盖最早的数据。如果slave断开的时间过长,导致数据被覆盖,则无法实现增量同步,只能再次进行全量同步。
3.主从同步优化
- 在master中配置repl-diskless-sync yes,启用无磁盘复制,避免全量同步时的IO。减少磁盘的读写,可以有效提高同步的速度
- Redis单节点上的内存占用不用太大,减少RDB导致的过多磁盘读写
- 适当提高
repl_baklog
的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步 - 限制一个
master
上的slave节点数量,如果实在slave
太多,则可以通过主-从-从链式结构,减少master
的压力
#当带宽大时,可考虑通过启用无磁盘复制来提高同步的速度
repl-diskless-sync yes
Redis的主-从-从
集群结构:
4.小结
全量同步与增量同步的区别:
- 全量同步:master将完整内存数据生成RDB,发送RDB给slave,后续命令持续记录在repl_baklog中,逐个发送给slave
- 增量同步:slave提交自己的
offset
到master,master获取repl_baklog中从offset
之后的命令给slave
什么情景下进行全量同步?
- slave第一次连接master节点时
- slave断开连接太久,repl_baklog中的数据已经被覆盖时
什么时候做增量同步?
- slave节点断开之后重新连接,并且在repl_baklog中能找到
offset
时
四 Redis哨兵
1.哨兵的作用和原理
Redist提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。
1.Sentinel的作用如下:
- 服务状态监控:不间断的检查Redis的master和slave是否按照预期工作
- 故障恢复:如果master故障,则Sentinel会将一个slave提升为master,当故障恢复后也以新的master为主(实现主从的切换)
- 通知:Sentinel充当Redis客户端的服务发现开源,当集群发生故障转移时,会将最新消息推送给Redis客户端
2.服务状态监控的过程:
Sentinel基于心跳机制检测服务状态,每隔1s
向集群中的每个示例发送ping命令
- 主观下线:如果Sentinel节点发现某个实例未在规定时间内响应,则认为该示例主观下线
- 客观下线:若超过指定数量(quorum)的Sentinel都认为该实例主观下线,则该实例客观下线。
quorum
值最好超过Sentinel实例的一半
3.选举新的master:
- 判断slave与master节点断开的时长,超过指定时长(
down-after-milliseconds*10
)则会排除该slave节点 - 判断slave节点的
slave-priority
值,越小则优先级越高,为0时则不参与选举 - 如果slave节点的
slave-priority
的值一样,则判断slave节点的offset
值,越大说明越新,优先级也越高 - 最后判断slave节点的运行id大小,越小优先级越高
4.故障转移
2.搭建Redis哨兵集群
创建一个sentinel.conf,或者在Redis中的解压文件中找到sentinel.conf,并且将它分别复制到3个redis实例的目录下7001 7002 7003
可以使用一下快捷复制命令,注意,要在7001 7002 7003的父级目录下使用一下命令
echo 7001 7002 7003 | xargs -t -n 1 cp /usr/local/redis-6.0.6/sentinel.conf
然后修改相关配置参数
# 指定哨兵的端口号
port 27001
# 指定当前哨兵节点的IP地址
sentinel announce-ip 192.168.119.101
# 指定主节点哨兵的实例名称(这里是mymaster),及主节点IP,和主观下线值(quorum),超过这个值则重新选举主节点
sentinel monitor mymaster 192.168.119.101 7001 2
# 主节点的最大断开时长
sentinel down-after-milliseconds mymaster 5000
# 故障恢复时间
sentinel failover-timeout mymaster 60000
# 是否后台运行
daemonize yes
# 指定工作路径,即sentinel.conf的目录
dir "/tmp/7001"
# 指定日志文件目录
logfile ""
一键修改全部实例的命令
sed -i -e 's/26379/27001/g' \
-e 's/dir \/tmp/dir "\/tmp\/7001" /g' \
-e 's/daemonize no/daemonize yes/g' \
-e 's/logfile ""/logfile "\/tmp\/7001\/sentinel.log"/g' \
-e 's/127.0.0.1 6379 2/192.168.119.101 7001 2/g' \
7001/sentinel.conf;
sed -i -e 's/26379/27002/g' \
-e 's/dir \/tmp/dir "\/tmp\/7002" /g' \
-e 's/daemonize no/daemonize yes/g' \
-e 's/logfile ""/logfile "\/tmp\/7002\/sentinel.log"/g' \
-e 's/127.0.0.1 6379 2/192.168.119.101 7001 2/g' \
7002/sentinel.conf;
sed -i -e 's/26379/27003/g' \
-e 's/dir \/tmp/dir "\/tmp\/7003" /g' \
-e 's/daemonize no/daemonize yes/g' \
-e 's/logfile ""/logfile "\/tmp\/7003\/sentinel.log"/g' \
-e 's/127.0.0.1 6379 2/192.168.119.101 7001 2/g' \
7003/sentinel.conf;
一键添加哨兵配置
sed -i '1a sentinel announce-ip 192.168.119.101' \
7001/sentinel.conf;
sed -i '1a sentinel announce-ip 192.168.119.102' \
7002/sentinel.conf;
sed -i '1a sentinel announce-ip 192.168.119.103' \
7003/sentinel.conf;
一键运行哨兵
redis-sentinel 7001/sentinel.conf;
redis-sentinel 7002/sentinel.conf;
redis-sentinel 7003/sentinel.conf;
通过 ps -ef|grep redis
查看,可以看到3个sentinel实例,和3个redis实例均已运行
4.RedisTemplate哨兵模式
在Sentinel集群监管下的Redis主从集群,其节点会因为自动故障转移而发生改变,Redis的客户端必须感知这种变化,及时更新连接信息。Spring的RedisTemplate底层利用lettuce框架
实现了节点的感知和自动切换。
1.引入RedisTemplate
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置Redis的读写分离
@Bean
public LettuceClientConfigurationBuilderCustomizer configurationBuilderCustomizer(){
return clientConfigurationBuilder ->clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}
此处的ReadFrom是一个枚举,用来配置Redis的读取策略,包括下面选择:
- MASTER:从主节点读取
- MASTER_PREFERRED:优先读取主节点,主节点不可用时才读取从节点
- REPLICA:从replica节点读取
- REPLICA_PREFERRED:优先从replica节点读取,从节点不可用时从主节点读取
3.修改配置文件
spring:
redis:
sentinel:
# 配置主节点名称
master: mymaster
# 配置redis-sentinel集群节点
nodes:
- 192.168.119.101:27001
- 192.168.119.101:27002
- 192.168.119.101:27003
五 Redis分片集群
1.分片集群结构
主从和哨兵可以解决高可用,高并发读取的问题。但是依然有两个问题没有解决:
- 海量数据存储问题
- 高并发写入的问题
可以考虑使用分片集群来解决上述问题,分片集群的特征:
- 集群中有多个master,每个master保存不同的数据
- 每个master都可以有多个slave节点
- master之间通过ping检测彼此的健康状态
- 客户端请求可以访问集群中的任意节点,最终都会被转发到正确可用的节点
2.分片集群搭建
分片集群数量较多,以最小的分片集群作为示例演示,包含3个master,其中每一个master节点包含一个slave节点
2.集群搭建准备,之前搭建集群和哨兵的目录可以删除掉
# 1.创建文件夹
mkdir 7001 7002 7003 8001 8002 8003
# 2.拷贝配置文件
echo 7001 7002 7003 8001 8002 8003 | xargs -t -n 1 cp /usr/local/redis-6.0.6/redis.conf
3.关键参数配置
port 7001
#开启集群配置
cluster-enabled yes
#节点配置文件,无需创建,由Redis自行维护,放开即可
cluster-config-file nodes-6379.conf
#节点心跳失败的超时时间
cluster-node-timeout 15000
#持久化文件存放目录,可根据实际情况修改
dir /data/
#绑定地址,允许远程访问
bind 0.0.0.0
#后台运行
daemonize yes
#注册的实例ip及端口号,根据实际情况修改
replica-announce-ip 192.168.119.101
replica-announce-port 7001
#保护模式
protected-mode no
#数据库数量,一般情况下修改为1即可,默认情况下为16
databases 1
# 日志,配置日志文件的位置及名称
logfile /tmp/7001/cluster.log
4.运行Redis集群实例
redis-server 7001/redis.conf;
redis-server 7002/redis.conf;
redis-server 7003/redis.conf;
redis-server 8001/redis.conf;
redis-server 8002/redis.conf;
redis-server 8003/redis.conf;
5.此时的 Redis 实例已经全部运行,但是之间并无直接关联,现在还需要去创建集群。运行以下命令创建集群
redis-cli --cluster create --cluster-replicas 1 \
192.168.119.101:7001 \
192.168.119.101:7002 \
192.168.119.101:7003 \
192.168.119.101:8001 \
192.168.119.101:8002 \
192.168.119.101:8003
redis-cli --cluster
:Redis的集群操作命令create
:创建集群--cluster-replicas 1
:指定集群中每个master的副本数量为 1,此时节点数 ÷ ( replica+1 )
得到的就是master的数量。因此节点列表前n个就是,master,其他节点都是slave,随机分配到不同的master
运行之后,redis会给出一个集群方案
确定这个集群分案没有问题后,则输入 yes
,至此,分片集群创建完成。
6.检查集群状态,可以通过主节点的端口号查看集群状态,下面以 7001 作为示例
redis-cli -p 7001 cluster nodes
3.散列插槽
Redis会把每一个master节点映射到【0~16383】共计16384个插槽( hash slot)上,查看集群信息时就能看到,参考上图。
1.Redis中的数据 key 不与节点绑定
Redis中的数据 key 不与节点绑定,而是跟插槽绑定。redis 会根据 key 的有效部分计算插槽值。有以下两种情况:
- key 中包含 “{}” ,且 “{}” 中至少包含一个字符,“{}” 中的部分是有效的
- key 中不包含 “{}” ,整个 key 都是有效部分
例如:key 值为 num, 那么就根据 num 值进行计算,如果是 { shawn } num,则根据 shawn 计算。计算方法为利用CRC16算法
得到一个哈希值,然后对16383取余,最后得到的结果就是 slot 值
2.集群模式下进入Redis
进入指定端口的集群存测试数据存储
redis-cli -c -p [port]
# 示例
redis-cli -c -p 7001
3.Redis如何判断某个 key 应该在哪个实例?
- 将 16384 个插槽分配到不同的实例
- 根据 key 的有效部分计算哈希值,然后对 16384 取余
- 余数作为插槽,寻找插槽所在实例即可
4.如何将同一数据固定的保存在同一个Redis实例?
- 如果同一类数据计算出来的插槽是一样,则可以保证它们都存储在同一个Redis实例中
- 只需要保证 key 的有效部分一样即可,例如 key 都以 {typeId}为前缀
4.集群伸缩
redis-cli --cluster
中提供了很多方法,可以通过一下命令查看
# 查看 redis-cli --cluster 命令帮助
redis-cli --cluster help
# 添加节点
redis-cli --cluster add-node [new_host]:[new_port] [exist_host]:[exist_port]
准备一个8004的文件夹,和 redis 配置文件,修改端口号等相关配置参数,然后运行该实例
#运行 8004 实例
redis-server 8004/redis.conf
1.添加为主节点
#1.不加任何选项时默认添加的为主节点(master),且不分配插槽
redis-cli --cluster add-node 192.168.119.101:8004 192.168.119.101:7001
#2.为指定节点分配插槽,使用 reshard 命令
redis-cli --cluster reshard 192.168.119.101:7001
输入要分配的插槽数量,这里我们输入 3000 ,然后敲击回车
输入要接收插槽的节点ID,我们通过命令查询到,输入即可
输入想要获取的插槽所在的节点Id,这里我们从 7002 中拿出3000个插槽分配给 8004,这里输入 7002 的节点Id,
从多个节点分配时,输入多个,结束时输入 done
即可,
等待分配完成,查看集群状态
此时,原本在 7002 节点上的 【6462 - 9461】插槽 被迁移到了 8004 节点,且上面的数据也被迁移了过来。
这也说明了,Redis分片集群中的数据,是和插槽绑定的,而非节点
2.添加为从节点
# 增加 --cluster-slave 选项,添加进去的 8004 节点将变成从节点,并且指定主节点的ID(通过redis-cli -p 7001 cluster nodes查询)
redis-cli --cluster add-node 192.168.119.101:8004 192.168.119.101:7001 \
--cluster-slave \
--cluster-master-id 94a1478400df69fcf0e197fb6a129a39298f359b
通过redis-cli -p 7001 cluster nodes
查询,可以看到 7001 下面有两个从节点
更多命令通过 redis-cli --cluster help
查看用法。
3.删除指定节点
删除从节点时使用命令直接删除即可,删除主节点时需要把主节点上的插槽转移到其他节点上方可删除
# 语法示例
redis-cli --cluster del-node [host]:[port] 【nodeId】
# 删除示例
redis-cli --cluster del-node 192.168.119.101:8004 e12ee3d58e459db6272750dafef410ed5d632f8c
5.故障转移
尝试关闭或重启指定集群,然后观察日志
# 关闭指定集群,以 7002 为例
redis-cli -p 7002 shutdown
# 重启指定集群,以 7002 为例
redis-server 7002/redis.conf
会发现原来的从节点变成了主节点,而重启之后的 7002 实例,变成了 从节点,成功完成了主从切换,实现了故障转移
6.手动故障转移
利用 cluster failover 命令手动让集群中的某个master节点宕机,切换到执行 cluster failover 命令的这个slave节点上,实现无感知的数据迁移。
手动的FialOver命令支持三种不同模式
- 默认模式:流程如上图
- force:省略了对 offset 的校验
- takeover:直接执行第5步,忽略数据一致性,忽略 master 状态 和其他 master 意见
具体实现:
以 7002 示例为例,现在的 7002 已经是从节点,而它的主节点为 8001
连接 7002 ,输入 cluster fialover 命令,实现主从切换
# 1.连接 7002 实例
redis-cli -c -p 7002
# 2.输入命令 cluster fialover
cluster fialover
# 3.退出 exit
exit
# 4.查看集群状态
redis-cli -p 7001 cluster nodes
可以看到,此时的 7002 已经变成了 master 节点,而 8001 则变成了 slave 节点,从而实现手动故障转移
7.RestTemplate访问分片集群
RedisTemplate底层基于 lettuce 实现了分片集群的支持,而使用的步骤基本与哨兵模式一致
1.引入redis的starter依赖
2.配置分片集群地址
spring:
redis:
cluster:
# 指定分片集群中的每一个节点信息
nodes:
- 192.168.119.101:7001
- 192.168.119.101:7002
- 192.168.119.101:7003
- 192.168.119.101:8001
- 192.168.119.101:8002
- 192.168.119.101:8003
- 192.168.119.101:8004
3.配置读写分离