【高级篇】
接下来继续学习Redis的高级篇,内容包含:分布式缓存 、多级缓存 、Redis最佳实践 相关内容
本篇主要是介绍Redis的分布式缓存,为了解决单点Redis的问题,具体有:用RDB和AOF解决Redis的持久化问题 ,Redis的主从集群 ,Redis的哨兵 以及分片集群
欢迎大家一起学习,共同进步🥰
文章目录
1.分布式缓存
单点Redis的问题
-
数据丢失问题:Redis是内存储存,服务器重启可能会丢失数据
++解决方法++:实现Redis数据持久化
-
并发能力问题
++解决方法++:搭建主从集群,实现读写分离
-
故障恢复问题:如果Redis宕机则服务不可用,需要一种自动的故障恢复手段
++解决方法++:利用Redis哨兵,实现健康检测和自动恢复
-
存储能力问题
++解决方法++:搭建分片集群,利用插槽机制实现动态扩容
1.1Redis持久化
RDB
Redis Database Backup file(Redis数据备份文件),也叫Redis数据快照
简单来说就是把内存中的所有数据都记录到磁盘中。当Redis故障重启后,从磁盘读取快照文件,恢复数据。
快照文件称为RDB文件,默认是存储在当前目录下

Redis停机时会执行一次RDB

Redis的fork原理:
bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入RDB文件
fork采用的是copy-on-write技术:
- 当主进程执行读操作时,访问共享内存
- 当主进程执行写操作是,则会拷贝一份数据,执行写操作

总结
RDB方式bgsave的基本流程?
- fork主进程得到一个子进程,共享内存空间
- 子进程读取内存数据并写入新的RDB文件
- 用新的RDB 文件替换旧的RDB文件
RDB会在什么时候执行?save 60 1000代表什么?
- 默认是服务停止时进行
- 代表60秒至少执行1000次修改则触发RDB
RDB的缺点?
- RDB执行间隔时间长,两次RDB之间写入数据有丢失的风险
- fork子进程、压缩、写出RDB文件都比较耗时
AOF
AOF全称Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件

AOF默认是关闭的,需要修改redis.conf配置文件来开启AOF:
bash
# 是否开启AOF功能
appendonly yes
# AOF文件的名称
appendfilename "appendonly.aof"
AOF的命令记录的评率也可以通过redis.conf文件进行配置:

因为是记录命令,AOF文件会比RDB文件大的多,而且AOF会记录对同一个key的多次写操作,但只有最后一次写操作才有意义。通过执行bgrewriteaof命令,可以让文件执行重写功能,用最少的命令达到相同的效果

对比
RDB和AOF各有自己的优缺点,如果对数据安全性要求较高,在实际开发中往往会结合两者来使用

1.2Redis的主从
1.2.1主从集群结构
单节点Redis的并发能力有限,要进一步提高Redis的并发能力,急需要搭建主从集群,实现读写分离

在实际应用中,对Redis的读操作比写操作要多,所以对master进行读操作,对其他slave进行写操作,同时master要对slave进行数据同步
1.2.2搭建主从集群
在同一台虚拟机上开启三个Redis实例,模拟主从集群





开启主从关系:
-
修改配置文件(永久生效)
在redis.conf文件中添加一行配置
slaveof <masterip> <masterport> -
使用redis-cli客户端连接到redis服务,执行slaveof命令(重启后失效)
slaveof <masterip> <masterport>
查看集群状态信息:
INFO replication
1.2.3主从的全量同步原理
主从第一次 同步是全量同步:

master怎么知道是第一次同步?
Replication Id:简称replid,是数据集的标记,Id一致则说明是用一个数据集,每一个master都有唯一的replid,slave则会继承master的replidoffset:偏移量,随着记录在repl_baklog这个数据增多而增大,slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新因此slave做数据同步,必须向master声明自己的replid和offset,master才可以判断需要同步哪些数据
判断replid是否一样就能判断是否是第一次同步。
简述全量同步的流程?
- slave节点请求增量同步
- master节点判断replid,发现不一致,拒绝增量同步
- master将完整内存数据生成RDB,发送RDB到slave
- slave清空本地数据,加载master的RDB
- master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave
- slave执行接收到的命令,保持与master之间的同步
1.2.4主从的增量同步原理
主从第一次同步时全量同步,如果slave重启后同步则执行增量同步

repl_backlog(Replication Backlog)在 Redis 的底层实现中,本质上就是一个固定大小的循环队列
注意:repl_baklog大小有上限,写满后会覆盖最早的数据。如果slave宕机时间太久,导致尚未备份的数据被覆盖,则无法基于log做增量同步,只能再次全量同步
优化主从集群:
- 优化全量同步
- 在master中配置
repl-diskless-sync yes启用无磁盘复制,避免全量同步时的磁盘IO - Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO
- 在master中配置
- 尽量减少全量同步
- 适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
- 限制一个master上的slave节点数量,如果实在太多slave,则可以采用主-从-从链式结构,减少master压力

总结

1.3Redis哨兵
1.3.1哨兵的作用和工作原理
用来实现主从集群的自动故障恢复
作用:
- 监控:哨兵会不断检查master和slave是否按预期工作
- 自动故障恢复:如果master故障,哨兵会将一个slave提升为master。当故障实例恢复后也以新的master为主
- 通知:哨兵充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端

哨兵如何得知每个节点的状态?
服务状态监控
哨兵基于心跳机制监测服务状态,每1秒向集群的每个实例发送ping命令:
- 主观下线:如果某哨兵节点发现某实例未在规定时间响应,则认为该实例主观下线
- 客观下线:若超过指定数量(quorum)的哨兵都认为该实例下线,则该实例客观下线,quorum值最好超过哨兵实例数量的一半
选举新的master
一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据是这样的:
- 首先会判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds * 10)则会排除该slave节点
- 然后判断slave节点的slave-priority值,越小优先级越高,如果是0则永不参与选举
- 如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高
- 最后是判断slave节点的运行id大小,越小优先级越高。
实现故障转移
让被选中的slave变成master,广播让其他slave跟从新的master,让旧master变成slave

监控对象
1. 监控 Master(主节点)
这是哨兵最核心的任务。哨兵会持续检查 Master 是否存活,主要为了:
- 故障发现:判断 Master 是否进入了"主观下线"或"客观下线"状态。
- 自动故障转移:一旦 Master 彻底不可用,哨兵需要从 Slave 中选举一个新的 Master 来顶替它。
2. 监控 Slave(从节点)
哨兵同样会定期检查所有 Slave 节点的状态,原因如下:
- 故障转移的候选池:当 Master 挂掉时,哨兵需要从健康的 Slave 中挑选一个数据最完整、优先级最高的节点升级为新的 Master。如果哨兵不监控 Slave,就无法知道哪些节点是健康的,也就无法完成自动切换。
- 感知拓扑变化:哨兵会自动发现新加入的 Slave 节点,并将其纳入监控范围。
3. 监控其他 Sentinel(哨兵节点)
哨兵通常是以集群模式部署的(一般至少 3 个节点),它们之间也会互相监控:
- 互相发现 :哨兵通过 Master 节点上的
__sentinel__:hello频道进行通信,发现彼此的存在。- 达成共识:单个哨兵认为 Master 下线只是"主观下线",必须通过哨兵间的通信和投票,达到法定人数(Quorum)确认,才会判定为"客观下线"并触发故障转移。
1.3.2搭建哨兵集群
-
创建三个哨兵实例
-
在配置文件指定监控信息

1.3.3RedisTemplate连接哨兵
在哨兵Sentinel集群监管下的Redis主从集群,其节点会因为自动故障转移而发生变化,Redis的客户端必须感知这一变化及时更新连接信息。
Spring的RedisTemplate底层利用lettuce实现了节点的感知和自动切换
-
在pom文件中引入Redis的starter依赖:
xml<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> -
然后再配置文件application.yml中指定sentinel相关信息
ymlspring: redis: sentinel: master: mymaster # 指定master名称 nodes: # 指定redis-sentinel集群信息 - 192.168.150.101:27001 - 192.168.150.101:27002 - 192.168.150.101:27003 -
配置主从读写分离
java@Bean public LettuceClientConfigurationBuilderCustomizer configurationBuilderCustomizer(){ return configBuilder -> configBuilder.readFrom(ReadFrom.REPLICA_PREFERRED); }这里的ReadFrom是配置Redis的读取策略,是一个枚举,包括下面选择:
- MASTER:从主节点读取
- MASTER_PREFERRED:优先从master节点读取,master不可用才读取replica
- REPLICA:从slave(replica)节点读取
- REPLICA_PREFERRED:优先从slave(replica)节点读取,所有的slave都不可用才读取master
1.4Redis分片集群
分片集群结构

主从哨兵可以解决高可用、高并发读的问题,但仍然有两个问题:
- 海量数据存储问题
- 高并发写问题
使用分片集群可以解决,分片集群的特征:
- 集群中有多个master,每个master保存不同数据
- 每个master都可以有多个slave节点
- master之间通过ping监测彼此健康状态
- 客户端请求可以访问集群任意节点,最终都会被转发到正确节点
1.4.1搭建分片集群
核心步骤:

具体过程 :准备环境 → 配置节点 → 启动实例 → 组装集群
-
准备目录和配置文件
-
创建目录
-
编写配置文件(以一个节点为例)
bash# 端口号 (7001~7006 分别对应修改) port 7001 # 允许远程访问 bind 0.0.0.0 # 开启集群模式 (必须) cluster-enabled yes # 集群配置文件 (自动生成,无需手动修改) cluster-config-file nodes-7001.conf # 节点超时时间 (毫秒) cluster-node-timeout 15000 # 开启持久化 (推荐 AOF) appendonly yes # 设置密码 (生产环境强烈建议开启,所有节点密码需一致) # requirepass yourpassword # masterauth yourpassword # 后台运行 daemonize yes
-
-
启动所有Redis节点
bashredis-server /opt/redis-cluster/7001/redis.conf redis-server /opt/redis-cluster/7002/redis.conf redis-server /opt/redis-cluster/7003/redis.conf redis-server /opt/redis-cluster/7004/redis.conf redis-server /opt/redis-cluster/7005/redis.conf redis-server /opt/redis-cluster/7006/redis.conf验证启动:
ps -ef | grep redis -
创建并组装集群
使用
redis-cli工具自动完成主从关系的分配和槽位(Slot)的分配bashredis-cli --cluster create \ 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 \ 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 \ --cluster-replicas 1 -
验证集群状态
-
连接集群
bashredis-cli -c -p 7001 ```<websource>source_group_web_9</websource> -
查看集群信息
bashcluster info -
查看节点拓扑
bashcluster nodes
-
1.4.2散列插槽
插槽(Slot)是 Redis 集群用来管理数据和定位数据的"逻辑地图"。虽然数据最终确实是存在 Master 节点里,但引入"插槽"这一层中间抽象,是为了解决"如何高效地找到数据"以及"如何灵活地调整数据分布"这两个难题

Redis如何判断某个key应该在哪个实例?
- 将16384个插槽分配到不同的实例
- 根据key的有效部分计算哈希值,对16384取余
- 余数作为插槽,寻找插槽对应的实例
如何将同一类数据固定的保存在同一个Redis实例?
- 让这一类数据使用相同的有效部分,例如key都以
{typeId}为前缀
1.4.3集群伸缩
集群伸缩:可以动态添加或减少节点
添加节点:
bash
redis-cli --cluster add-node <新节点IP:端口> <现有集群中任意节点IP:端口>
重新分配插槽:
-
启动重新分片
:对集群中任意节点执行以下命令:
bashredis-cli --cluster reshard <现有集群中任意节点IP:端口> -
按提示交互操作:
- 输入要迁移的插槽数量 :系统会询问
How many slots do you want to move...?。你需要计算并输入一个数值,例如,如果希望数据均匀分布,可以计划将总插槽数(16384)除以新的主节点总数。 - 输入接收插槽的目标节点 ID :系统会询问
What is the receiving node ID?。输入你在步骤二末尾记下的新节点的 ID。 - 指定插槽来源:系统会询问
Please enter all the source node IDs...- 输入
all表示从所有现有的主节点中均衡地迁移插槽(推荐)。 - 也可以输入特定源节点的 ID,然后输入
done来指定从哪些节点迁移。
- 输入
- 输入要迁移的插槽数量 :系统会询问
-
确认并执行 :系统会显示迁移计划,询问
Do you want to proceed with the proposed reshard plan?。输入yes确认,Redis 就会开始在后台异步迁移插槽和对应的数据。
1.4.4故障转移
当集群中一个master宕机时
-
故障检测
- 主观下线:集群中每个节点都会通过心跳ping/pong机制互相检测
- 客观下线:当超过半数的主节点都将该节点标记为 PFAIL 后,这个状态就会升级为"确认下线"(FAIL - Confirmed Failure),并通过 Gossip 协议广播给整个集群
-
从节点选举
一旦主节点被标记为 FAIL,其所有从节点(Replica)会开始竞争成为新的主节点
- 发起选举 :每个从节点会延迟一个随机的短暂时间后,向集群中所有主节点发送
FAILOVER_AUTH_REQUEST投票请求 - 投票
- 胜出条件:为了赢得选举,一个从节点必须获得超过半数主节点的选票
- 发起选举 :每个从节点会延迟一个随机的短暂时间后,向集群中所有主节点发送
-
故障转移与晋升
选出来的新master会:
- 晋升为主 :执行
SLAVEOF NO ONE命令,将自己提升为新的主节点。 - 接管插槽:接管原主节点负责的所有哈希槽(hash slots)。
- 广播身份:向整个集群广播 PONG 消息,宣告自己已成为新的主节点,并告知其负责的插槽信息。其他节点收到后会更新自己的集群拓扑信息。
- 晋升为主 :执行
-
客户端重定向
- 请求失败 :在故障转移期间,发往已宕机主节点的请求可能会收到
CLUSTERDOWN错误或连接超时的响应。 - 自动重定向 :具备集群感知能力的客户端(如 Jedis Cluster, Lettuce)在捕获到异常后,会主动查询集群最新的拓扑结构(通过
CLUSTER SLOTS命令),获取新主节点的地址,并将后续请求自动重定向过去。
- 请求失败 :在故障转移期间,发往已宕机主节点的请求可能会收到
Redis 分片集群(Redis Cluster)没有哨兵(Sentinel) ,它依靠的是集群内部每个节点互相监测的机制来保证高可用
数据迁移:

1.4.5RedisTemplate访问分片集群
RedisTemplate底层同样基于lettuce实现了分片集群的支持,使用的步骤与哨兵模式基本一致:
- 引入Redis的starter依赖
- 配置分片集群地址
- 配置读写分离
分片集群的配置与哨兵模式不同
yml
spring:
redis:
cluster:
nodes:# 指定分片集群的每一个节点信息
- 192.168.150.101.7001
- 192.168.150.101.7002
- 192.168.150.101.7003
- 192.168.150.101.7004
