【Redis|高级篇1】分布式缓存|持久化(RDB、AOF)、主从集群、哨兵、分片集群

【高级篇】

接下来继续学习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的replid
  • offset:偏移量,随着记录在repl_baklog这个数据增多而增大,slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新

因此slave做数据同步,必须向master声明自己的replid和offset,master才可以判断需要同步哪些数据

判断replid是否一样就能判断是否是第一次同步。

简述全量同步的流程?

  1. slave节点请求增量同步
  2. master节点判断replid,发现不一致,拒绝增量同步
  3. master将完整内存数据生成RDB,发送RDB到slave
  4. slave清空本地数据,加载master的RDB
  5. master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave
  6. slave执行接收到的命令,保持与master之间的同步
1.2.4主从的增量同步原理

主从第一次同步时全量同步,如果slave重启后同步则执行增量同步

repl_backlog(Replication Backlog)在 Redis 的底层实现中,本质上就是一个固定大小的循环队列

注意:repl_baklog大小有上限,写满后会覆盖最早的数据。如果slave宕机时间太久,导致尚未备份的数据被覆盖,则无法基于log做增量同步,只能再次全量同步

优化主从集群:

  • 优化全量同步
    • 在master中配置repl-diskless-sync yes启用无磁盘复制,避免全量同步时的磁盘IO
    • Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO
  • 尽量减少全量同步
    • 适当提高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. 创建三个哨兵实例

  2. 在配置文件指定监控信息

1.3.3RedisTemplate连接哨兵

在哨兵Sentinel集群监管下的Redis主从集群,其节点会因为自动故障转移而发生变化,Redis的客户端必须感知这一变化及时更新连接信息。

Spring的RedisTemplate底层利用lettuce实现了节点的感知和自动切换

  1. 在pom文件中引入Redis的starter依赖:

    xml 复制代码
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
  2. 然后再配置文件application.yml中指定sentinel相关信息

    yml 复制代码
    spring:
      redis:
        sentinel:
          master: mymaster # 指定master名称
          nodes: # 指定redis-sentinel集群信息
            - 192.168.150.101:27001
            - 192.168.150.101:27002
            - 192.168.150.101:27003
  3. 配置主从读写分离

    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搭建分片集群

核心步骤

具体过程准备环境 → 配置节点 → 启动实例 → 组装集群

  1. 准备目录和配置文件

    1. 创建目录

    2. 编写配置文件(以一个节点为例)

      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
  2. 启动所有Redis节点

    bash 复制代码
    redis-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

  3. 创建并组装集群

    使用 redis-cli 工具自动完成主从关系的分配和槽位(Slot)的分配

    bash 复制代码
    redis-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
  4. 验证集群状态

    1. 连接集群

      bash 复制代码
      redis-cli -c -p 7001
      ```<websource>source_group_web_9</websource>
    2. 查看集群信息

      bash 复制代码
      cluster info
    3. 查看节点拓扑

      bash 复制代码
      cluster 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:端口>

重新分配插槽

  1. 启动重新分片

    :对集群中任意节点执行以下命令:

    bash 复制代码
    redis-cli --cluster reshard <现有集群中任意节点IP:端口>
  2. 按提示交互操作:

    • 输入要迁移的插槽数量 :系统会询问 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 来指定从哪些节点迁移。
  3. 确认并执行 :系统会显示迁移计划,询问 Do you want to proceed with the proposed reshard plan?。输入 yes 确认,Redis 就会开始在后台异步迁移插槽和对应的数据。

1.4.4故障转移

当集群中一个master宕机时

  1. 故障检测

    1. 主观下线:集群中每个节点都会通过心跳ping/pong机制互相检测
    2. 客观下线:当超过半数的主节点都将该节点标记为 PFAIL 后,这个状态就会升级为"确认下线"(FAIL - Confirmed Failure),并通过 Gossip 协议广播给整个集群
  2. 从节点选举

    一旦主节点被标记为 FAIL,其所有从节点(Replica)会开始竞争成为新的主节点

    1. 发起选举 :每个从节点会延迟一个随机的短暂时间后,向集群中所有主节点发送 FAILOVER_AUTH_REQUEST 投票请求
    2. 投票
    3. 胜出条件:为了赢得选举,一个从节点必须获得超过半数主节点的选票
  3. 故障转移与晋升

    选出来的新master会:

    1. 晋升为主 :执行 SLAVEOF NO ONE 命令,将自己提升为新的主节点。
    2. 接管插槽:接管原主节点负责的所有哈希槽(hash slots)。
    3. 广播身份:向整个集群广播 PONG 消息,宣告自己已成为新的主节点,并告知其负责的插槽信息。其他节点收到后会更新自己的集群拓扑信息。
  4. 客户端重定向

    1. 请求失败 :在故障转移期间,发往已宕机主节点的请求可能会收到 CLUSTERDOWN 错误或连接超时的响应。
    2. 自动重定向 :具备集群感知能力的客户端(如 Jedis Cluster, Lettuce)在捕获到异常后,会主动查询集群最新的拓扑结构(通过 CLUSTER SLOTS 命令),获取新主节点的地址,并将后续请求自动重定向过去。

Redis 分片集群(Redis Cluster)没有哨兵(Sentinel) ,它依靠的是集群内部每个节点互相监测的机制来保证高可用

数据迁移

1.4.5RedisTemplate访问分片集群

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

  1. 引入Redis的starter依赖
  2. 配置分片集群地址
  3. 配置读写分离

分片集群的配置与哨兵模式不同

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
相关推荐
weixin_408099672 小时前
【实战教程】EasyClick 调用 OCR 文字识别 API(自动识别屏幕文字 + 完整示例代码)
前端·人工智能·后端·ocr·api·安卓·easyclick
PD我是你的真爱粉2 小时前
Redis 持久化、过期删除、淘汰策略与内存碎片全解析
java·redis·bootstrap
014-code2 小时前
rabbitmq消息积压:如何快速排查与处理
分布式·rabbitmq
添尹2 小时前
Go语言基础之指针
开发语言·后端·golang
斌味代码2 小时前
Redis 分库分表实战:从垂直拆分到水平扩容完整记录
数据库·redis·bootstrap
Percep_gan2 小时前
在芋道自定义数据权限
java·数据库
哆啦阿梦2 小时前
Java AI 应用工程师 - 完整技能清单
java·开发语言·人工智能
rchmin2 小时前
阿里Tair分布式锁与Redis分布式锁的实现区别
数据库·redis·分布式
VT LI2 小时前
Lua 源码执行流程全解析:词法分析、语法分析、字节码生成、虚拟机执行与垃圾回收
java·开发语言·lua