当前位置: 首页 > news >正文

#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文件。

  1. 触发 BGSAVE

    • 通过配置文件自动触发(如 save 60 10000)或手动执行 BGSAVE 命令。

  2. fork() 子进程

    • 主进程短暂阻塞(时间取决于内存页表大小),生成子进程。

  3. 子进程持久化

    • 子进程遍历内存数据,按二进制格式写入 RDB 文件。

    • 主进程继续处理请求,修改数据时触发 COW(copy-on-write)。

  4. 完成持久化

    • 子进程写入完成后退出,主进程清理临时文件并更新 RDB 文件。

从主进程fork到子进程的过程当中需要阻塞,我们需要尽可能的缩短

1 该流程图的简单解释:

  1. 主进程启动bgsave:当需要生成RDB快照时,主进程调用fork()创建一个子进程。

  2. 复制页表:子进程复制主进程的页表(内存地址映射表),此时两者共享同一份物理内存数据。

  3. 子进程写入RDB:子进程基于复制的页表,读取内存中的数据(如数据A、数据B),并将其写入新的RDB文件。

  4. 主进程继续运行:主进程仍能处理客户端请求。如果主进程修改了数据(例如修改数据A),操作系统会通过写时复制(COW)copy-on-write 复制被修改的内存页,主进程使用新副本,子进程仍用旧数据写入RDB。

  5. 替换旧文件:子进程完成RDB写入后,用新生成的RDB文件替换磁盘上的旧文件,持久化完成。

关键点

  • fork()只复制页表,不复制内存,速度快。

  • 写时复制保证子进程生成的是fork()瞬间的数据快照。

  • 主进程无阻塞,全程可正常响应请求。

2 页表的概念:

  • 作用
    页表记录了虚拟内存页(Page)与物理内存页框(Page Frame)的映射关系。每个进程拥有独立的页表,确保其虚拟地址空间与其他进程隔离。

  • 虚拟地址与物理地址的转换

    • 虚拟地址被划分为 页号(Page Number) 和 页内偏移(Offset)

    • 通过页号查找页表,得到对应的物理页框号(Frame Number),再结合页内偏移得到物理地址。(详单与页数与具体位置,以此来对应的查找到具体的物理内存地址)

3 RDB持久化优点:

  1. 高性能:fork子进程处理数据保存,主进程无阻塞。

  2. 紧凑备份:二进制快照文件小,适合全量备份与灾难恢复。

  3. 快速恢复:直接加载数据,比AOF日志重放更快(尤其大数据量时)。

4 RDB持久化缺点:

  1. 数据丢失风险:两次快照间的数据可能丢失(依赖配置的保存周期)。

  2. 潜在阻塞:数据集大时,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)建立连接后,主节点首先会判断是否为第一次同步。如果是初次同步,主节点会将自己的数据版本标识(包含replidoffset)返回给从节点,从节点保存这些信息以标识数据来源和进度。

紧接着,主节点通过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未改变),同步流程如下:

  1. ​从节点重启后发起同步请求​​:

    • 从节点向主节点发送 PSYNC 命令,携带自身的 replid 和 offset
    • 主节点检查 replid 是否一致:
      • 若一致,返回 +CONTINUE 响应,触发 ​​部分重同步​​。
      • 若不一致,触发全量同步。
  2. ​部分重同步流程​​:

    • 主节点通过 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 个散列插槽,每个键通过算法分配到唯一插槽,实现数据分片存储。

工作机制
  1. 键到插槽的映射

    • 计算公式:HASH_SLOT = CRC16(key) % 16384

    • 示例:键 user:1001 的 CRC16 值为 12345,则分配到插槽 12345 % 16384 = 12345

  2. 插槽分配到节点

    • 集群初始化时,管理员将 16384 个插槽手动或自动分配给各主节点(如 3 主节点各占约 5461 个槽)。

    • 节点负责存储其分配的槽对应的数据。

  3. 数据访问流程

    • 客户端请求键时,先计算插槽,再向负责该插槽的节点发起操作。

    • 若客户端连接的是错误节点,会收到 MOVED 重定向响应(包含正确节点地址)。

高效负载均衡,数据均匀分布,借助于动态哈希算法计算Key的归属插槽。在扩容或者出现宕机时,只需将部分插槽从现有节点迁移到新节点,无需全量数据重新分配

(3 集群伸缩

案例:难点插槽分配(7001的插槽分配给7004)

(4 故障转移

自动故障恢复
  1. 节点状态检测

    • 集群节点通过 Gossip 协议 定期交换心跳信息。

    • 若主节点 A 在超时时间内(默认 15 秒)无响应,其他节点将其标记为 PFAIL(疑似下线)。

  2. 故障确认

    • 当多数主节点确认主节点 A 不可达,将其标记为 FAIL(确认下线)。

  3. 从节点晋升

    • 主节点 A 的从节点发起选举,获得多数主节点投票后晋升为新主节点。

    • 客户端后续请求该插槽时,通过 MOVED 响应指向新主节点。

相关文章:

  • 基于大模型的全面惊厥性癫痫持续状态技术方案
  • 1.4 C++之运算符与表达式
  • 分类算法 Kmeans、KNN、Meanshift 实战
  • 从 0 到 1:用 Trae 插件 Builder 模式开发端午包粽子小游戏
  • 权限控制相关实现
  • 基于Flink的数据中台管理平台
  • 制作一款打飞机游戏53:子弹样式
  • 破解充电安全难题:智能终端的多重防护体系构建
  • Token类型与用途详解:数字身份的安全载体图谱
  • 项目中Warmup耗时高该如何操作处理
  • 在SpringBoot项目中,使用单元测试@Test
  • 数据库与Redis数据一致性解决方案
  • 25_05_19Linux实战篇、第一章_02若依前后端部署之路(前端)
  • nfs存储IO等待,导致k8s业务系统卡慢问题处理
  • 十四、Hive 视图 Lateral View
  • Linux 文件(3)
  • 算法第25天 | 491. 非递减子序列、46. 全排列、47. 全排列 II
  • C语言——函数递归与迭代
  • 【Java高阶面经:微服务篇】6.从机房到线程池:隔离机制如何成为高可用系统的“隐形护盾”?
  • 基于Android的XX校园交流APP