#Redis缓存篇#(七)分布式缓存
目录
一 单节点Redis
1 单节点的问题
二 分布式缓存
1 Redis持久化
(1 RDB持久化
(2 AOF持久化
2 Redis主从集群
(1 搭建主从架构
(2 主从数据同步原理
3 Redis哨兵
(1 哨兵的作用和原理
(2 搭建哨兵集群
(3 RedisTemplate的哨兵模式
4 Redis分片集群
(1 搭建分片集群
(2 散列插槽
核心作用
工作机制
(3 集群伸缩
(4 故障转移
自动故障恢复
一 单节点Redis
1 单节点的问题
二 分布式缓存
1 Redis持久化
(1 RDB持久化
RDB持久化(Redis Database Backup file)Redis数据备份文件,也被叫做Redis数据快照。简单来说就是把内存中所有数据都记录到磁盘当中。当Redis实例故障重启后,从磁盘当中读取快照文件,恢复数据。
- 在Redis停止那一刻自动触发一次save(停机时自动save)默认开启。
- 可在配置文件当中设置3600秒内如果有1个key被修改则执行bgsave(自动save)默认关闭。
其他的一些配置参数(是否压缩/修改保存文件名称/文件保存的目录)
save "" 禁用RDB持久化
fork机制
bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入RDB文件。
-
触发 BGSAVE:
-
通过配置文件自动触发(如
save 60 10000
)或手动执行BGSAVE
命令。
-
-
fork() 子进程:
-
主进程短暂阻塞(时间取决于内存页表大小),生成子进程。
-
-
子进程持久化:
-
子进程遍历内存数据,按二进制格式写入 RDB 文件。
-
主进程继续处理请求,修改数据时触发 COW(copy-on-write)。
-
-
完成持久化:
-
子进程写入完成后退出,主进程清理临时文件并更新 RDB 文件。
-
从主进程fork到子进程的过程当中需要阻塞,我们需要尽可能的缩短
1 该流程图的简单解释:
-
主进程启动
bgsave
:当需要生成RDB快照时,主进程调用fork()
创建一个子进程。 -
复制页表:子进程复制主进程的页表(内存地址映射表),此时两者共享同一份物理内存数据。
-
子进程写入RDB:子进程基于复制的页表,读取内存中的数据(如数据A、数据B),并将其写入新的RDB文件。
-
主进程继续运行:主进程仍能处理客户端请求。如果主进程修改了数据(例如修改数据A),操作系统会通过写时复制(COW)copy-on-write 复制被修改的内存页,主进程使用新副本,子进程仍用旧数据写入RDB。
-
替换旧文件:子进程完成RDB写入后,用新生成的RDB文件替换磁盘上的旧文件,持久化完成。
关键点:
-
fork()
只复制页表,不复制内存,速度快。 -
写时复制保证子进程生成的是
fork()
瞬间的数据快照。 -
主进程无阻塞,全程可正常响应请求。
2 页表的概念:
-
作用:
页表记录了虚拟内存页(Page)与物理内存页框(Page Frame)的映射关系。每个进程拥有独立的页表,确保其虚拟地址空间与其他进程隔离。 -
虚拟地址与物理地址的转换:
-
虚拟地址被划分为 页号(Page Number) 和 页内偏移(Offset)。
-
通过页号查找页表,得到对应的物理页框号(Frame Number),再结合页内偏移得到物理地址。(详单与页数与具体位置,以此来对应的查找到具体的物理内存地址)
-
3 RDB持久化优点:
-
高性能:fork子进程处理数据保存,主进程无阻塞。
-
紧凑备份:二进制快照文件小,适合全量备份与灾难恢复。
-
快速恢复:直接加载数据,比AOF日志重放更快(尤其大数据量时)。
4 RDB持久化缺点:
-
数据丢失风险:两次快照间的数据可能丢失(依赖配置的保存周期)。
-
潜在阻塞:数据集大时,fork子进程可能短暂影响主进程响应(尤其内存占满时)。
核心权衡:牺牲实时数据安全性,换取更高吞吐与恢复效率。
-
RDB适合:能接受“丢几分钟数据”但追求速度的场景,比如每天0点备份用户积分(丢几分钟积分无所谓,恢复快更重要)。
-
RDB不适合:银行转账系统(1秒都不能丢),这时得用AOF日志(记录每一笔操作)。
(2 AOF持久化
AOF全称位Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看作是命令日志文件。AOF是默认关闭的需要人为开启。
1 bgwrite
触发条件(后台异步执行)
-
手动触发:执行
BGREWRITEAOF
命令。 -
自动触发:根据
auto-aof-rewrite-percentage
和auto-aof-rewrite-min-size
配置(如 AOF 文件比上次重写后增长 100% 且大小超过 64MB)。
auto-aof-rewrite-percentage 100 # 比上次重写后增长100%时触发
auto-aof-rewrite-min-size 64mb # AOF文件至少达到64MB
思考:那两种方案各有优点为什么不把两种方案都开启呢?
这里是有版本的原因,在4.0版本之前两种同时开启是会出现两个文件,但是4.0版本之后支持混合持久化,将数据全部存储在aof的文件中,但是需要修改参数,开启AOF同时开启混合持久化。
# 开启AOF
appendonly yes
# 启用混合持久化(RDB头部 + AOF尾部)
aof-use-rdb-preamble yes
# 保留RDB的触发规则(可选)
save 900 1
-
同时开启 RDB+AOF(任何版本):
-
磁盘上会存在两个文件(
.rdb
和.aof
)。 -
恢复时需自行选择用 RDB 或 AOF(默认优先用 AOF)。
-
-
混合持久化(仅 4.0+):
-
只有一个
.aof
文件,但内部结构是 RDB 头部 + AOF 尾部。 -
恢复时自动结合两者优势,无需人工干预。
-
2 Redis主从集群
(1 搭建主从架构
实现:
创建三个文件夹用于管理并先将配置文件初始化
我们需要修改 1端口 2保存目录
修改每个实例的声明IP(虚拟机本身有多个ip为了避免将来混乱,在redis.conf文件当中指定每一个实例的绑定ip信息)
开启三个不同端口的redis
过程当中出现这个路径出错
现在三者之间还未形成主从的关系
设置7001这个端口的Redis为主节点(7002向7001发送请求请求同步)
设置7001这个端口的Redis为主节点(7003向7001发送请求请求同步)
查看7001的配置(7001为主节点同时两个slave子节点)
实现主从同步试验(这里的写只能在主节点当中,实现读写分离优化性能)
(2 主从数据同步原理
1 全量同步原理
“建立连接 → 版本校验 → 生成并传输RDB → 增量补漏”
当从节点(Slave)通过replicaof
命令与主节点(Master)建立连接后,主节点首先会判断是否为第一次同步。如果是初次同步,主节点会将自己的数据版本标识(包含replid
和offset
)返回给从节点,从节点保存这些信息以标识数据来源和进度。
紧接着,主节点通过bgsave
生成RDB快照(异步操作,避免阻塞主线程),将当前内存数据持久化为RDB文件并发送给从节点。从节点在接收到RDB后,清空本地旧数据并加载RDB文件,实现数据全量覆盖。
关键细节在于同步期间的写命令处理:主节点在生成RDB的过程中,依然会正常处理客户端的新写命令。这些命令会被暂存到复制缓冲区(repl_backlog)中,待RDB传输完成后,主节点再将缓冲区内的增量命令发送给从节点执行,最终确保主从数据完全一致。
主节点如何判断第一次同步?
repliid相同
Replication Id:简称replid,是数据集的标记,id一致则说明是同一数据集。每个Master都有唯一的replid,slave则会继承master节点的replid。
offset:偏移量,随着记录在repl_backlog中的数据增多而主键增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。
2 增量同步原理
当从节点(Slave)重启且主节点(Master)未发生变更时(replid
未改变),同步流程如下:
-
从节点重启后发起同步请求:
- 从节点向主节点发送
PSYNC
命令,携带自身的replid
和offset
。 - 主节点检查
replid
是否一致:- 若一致,返回
+CONTINUE
响应,触发 部分重同步。 - 若不一致,触发全量同步。
- 若一致,返回
- 从节点向主节点发送
-
部分重同步流程:
- 主节点通过
repl_backlog
环形缓冲区,找到从节点offset
之后的未同步命令。 - 将缺失的命令发送给从节点,从节点接收并执行,完成数据同步。
- 主节点通过
repl_backlog的大小有限,写满后会覆盖最早的数据。如果slave断开的时间过久,导致尚未备份的数据被覆盖,则无法基于log做增量同步,只能再次做全量同步。
3 命令传播原理
在主从集群在完成全量或增量同步后,主节点将后续新的写操作实时传递给节点的过程,确保主从数据持续一致。
客户端向主节点写入,主节点记录命令,主节点将命令写入缓冲区发送命令,从节点接受并执行
4 优化Redis主从集群同步的效率:
- 1 在 master 中配置repl-diskless-sync yes 启用无磁盘复制,避免全量同步时的磁盘IO(网络较好,磁盘读取较差)
- 2 Redis单节点的内存占用不要太大,减少RDB导致的过多磁盘IO。(设置合理的内存上限)
- 3 适量增大repl_backlog的大小,在出现slave宕机重启后可以快速重启恢复,尽可能避免全量同步。
- 4 限制一个master上的slave节点数量,如果实在是太多slave,则可以采用主-从-从链式结构,减少master的压力。
3 Redis哨兵
(1 哨兵的作用和原理
Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。
1 服务状态监控
Sentinel基于心跳机制检测服务状态,每隔一秒向集群的每个实例发送ping命令。
- 主观下线(SDOWN):如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
- 客观下线(ODOWN):若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。(类似投票)
综上所述:必须要先主管下线才可实现客观下线,主观下线时一个人觉得你下线而客观下线是很多人觉得你下线,同时,客观下线才是真正的下线。
2 选举新的master
一旦发现master故障,sentinel需要在slave中选择一个作为新的master,选择依据是这样的:
- 首先会判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds*10)则会排除该slave节点。
- 然后会判断slave节点的slave-priority值,越小优先级越高,如果是0则永不参与选举。
- 如果slave-prority一样。则判断slave节点的offset值,越大说明数据越新,优先级越高。
- 最后判断slave节点的运行id大小,越小优先级越高。
3 实现故障转移
当选中了其中一个slave为新的master后(例如slave1),故障的转移的步骤如下:
- sentinel给备选的slave1节点发送slaveof no one 命令,让该节点成为master
- sentinel给所有其他slave发送slaveof192.168.150.101 7002 命令,让这些slave成为新的master的从节点,开始从新的master上同步数据。
- 最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点。
(2 搭建哨兵集群
搭建一个三节点的Sentinel集群,来监管之前的Redis主从集群
三个sentinel实例的信息如下(ip根据自己的设置)
先创建文件夹
准备实例的配置文件(这里监控主节点master可以间接监控到slave节点的信息)
给对应的sentinel.conf 配置相关信息
port 27001
sentinel announce-ip 192.168.100.100
sentinel monitor mymaster 192.168.100.100 7001 2
sentinel down-after-milliseconds mymaster 500
sentinel failover-timeout mymaster 60000
dir "/tmp/s1"
port 27003
sentinel announce-ip 192.168.100.100
sentinel monitor mymaster 192.168.100.100 7001 2
sentinel down-after-milliseconds mymaster 500
sentinel failover-timeout mymaster 60000
dir "/tmp/s3"
port 27002
sentinel announce-ip 192.168.100.100
sentinel monitor mymaster 192.168.100.100 7001 2
sentinel down-after-milliseconds mymaster 500
sentinel failover-timeout mymaster 60000
dir "/tmp/s2"
实现:
需要先搭建主从集群并配置
这里的哨兵集群需要先切换到对应的tmp文件目录,然后分别执行
redis-sentinel s1/sentinel.conf
redis-sentinel s2/sentinel.conf
redis-sentinel s3/sentinel.conf 从而开启
现在我们测试把redis7001给断了
7002与7003会出现报错(连不上主节点了)
哨兵会将主节点转换给slave节点
(3 RedisTemplate的哨兵模式
Redis集群监管下的redis主从集群,其节点会因为自动故障而发生变化,Redis的刻划断必须感知这种变化,及时更新连接信息。Spring的RedisTemplate底层利用lettuce实现了节点的感知和自动转换。
4 Redis分片集群
(1 搭建分片集群
(2 散列插槽
核心作用
Redis 分片集群将数据划分为 16384 个散列插槽,每个键通过算法分配到唯一插槽,实现数据分片存储。
工作机制
-
键到插槽的映射
-
计算公式:
HASH_SLOT = CRC16(key) % 16384
-
示例:键
user:1001
的 CRC16 值为12345
,则分配到插槽12345 % 16384 = 12345
。
-
-
插槽分配到节点
-
集群初始化时,管理员将 16384 个插槽手动或自动分配给各主节点(如 3 主节点各占约 5461 个槽)。
-
节点负责存储其分配的槽对应的数据。
-
-
数据访问流程
-
客户端请求键时,先计算插槽,再向负责该插槽的节点发起操作。
-
若客户端连接的是错误节点,会收到
MOVED
重定向响应(包含正确节点地址)。
-
高效负载均衡,数据均匀分布,借助于动态哈希算法计算Key的归属插槽。在扩容或者出现宕机时,只需将部分插槽从现有节点迁移到新节点,无需全量数据重新分配
(3 集群伸缩
案例:难点插槽分配(7001的插槽分配给7004)
(4 故障转移
自动故障恢复
-
节点状态检测
-
集群节点通过 Gossip 协议 定期交换心跳信息。
-
若主节点 A 在超时时间内(默认 15 秒)无响应,其他节点将其标记为
PFAIL
(疑似下线)。
-
-
故障确认
-
当多数主节点确认主节点 A 不可达,将其标记为
FAIL
(确认下线)。
-
-
从节点晋升
-
主节点 A 的从节点发起选举,获得多数主节点投票后晋升为新主节点。
-
客户端后续请求该插槽时,通过
MOVED
响应指向新主节点。
-