Redis-高级篇(分布式缓存/持久化)

文章目录

Redis持久化

RDB

RDB也叫做Redis数据快照。就是把内存中的所有数据记录到磁盘中,当Redis实例故障重启后,从磁盘中读取文件,恢复数据。快照文件默认保存在当前运行目录。

通过如下命令可以使redis执行RDB

Redis内部也有触发RDB的机制

redis.conf文件中保存触发RDB触发的条件

bgsave执行原理

主线程开辟一个子线程,将对应的页表fork进子线程,子线程从页表获取到内存物理地址,然后进行读写到磁盘文件中

页表:记录内存物理地址和逻辑地址的表,该概念来自于操作系统,所有进程操作内存都是通过页表,操作逻辑地址去操作,而不是直接读写物理内存

注意:主进程接收新的写操作,不会直接写到物理内存中(因为这样会造成子线程的脏读),所以写的时候遵循copy-on-write,会先创建一个数据B副本,将数据B拷贝入数据B副本,然后对这个副本进行写操作,RDB执行完后再将副本拷贝入内存"

极端情况下,可能RDB没有完成,所有数据都创建了副本,相当于占用了原redis存储数据的两倍的空间

RDB缺点:RDB执行有时间间隔,两次RDB之间写入数据有丢失的风险;fork子进程,压缩,写入RDB文件都比较耗时。

AOF

Redis处理的每一个命令都会记录在AOF文件中,可以看作是命令日志文件,如下图:

AOF默认是关闭的,需要修改redis.conf配置文件来开启AOF

AOF的命令记录频率也可以通过reids.conf文件配置

不同配置项的优劣

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

也可以在redis.conf配置达到某个阈值时自动去重AOF文件(bgrewirteaof),如下图:

RDB 和 AOF区别

分布式缓存

redis主从架构

单节点的Redis并发能力有上限,要进一步提高Redis的并发能力,需要搭建主从集群,实现读写分离,向主节点写,去从节点读,主从节点实现数据同步,结构如下图:

创建三台redis实例有很多方法,比如docker。或者创建三个目录并在其中配置好redis.conf,分别启动就可以了,具体步骤不仔细讲解了,此时三个实例还没有任何关系,要配置主从可以使用replicaof或者saveof命令,有以下两种模式:

这里启动了三个实例,然后让7002和7003端口的实例作为7001实例的从节点

左上角是操作窗口,其他三个窗口分别的7001,7002,7003的启动窗口

查看集群效果

连接7001输入INFO replication命令

两个salve(从节点)

测试

在主节和从节点上分别执行写和读操作可以执行成功,再从节点上执行写操作就报错,可知读写分离成功,如下图

为什么从节点写入失败,是因为如果从节点可以执行写入操作,那么从节点的数据永远不可能与主节点数据一致,因为主节点无法通过从节点拷贝写入的数据。

数据同步

节点之间的数据同步

主从节点如何进行数据的同步

全量同步

主从第一次同步就是全量同步,流程如下

这个流程图挺清楚的

那master怎么判断slave是不是第一次来同步数据?

这里先介绍每一个redis都会有的两个属性,判断是否是第一次关键

从redis第一次同步的时候Replication Id肯定是和主节点不一致的

所以全量同步的过程就是如下图:slave来和master同步时,会携带replid和offset,replid不一致就说明是第一次来,然后master就会返回自己的replid和offest,slave记录这两个值,以后来时都携带上和master做增量同步。

第一阶段用具体代码代替原来的说明后

增量同步

主从第一次同步是全量同步,slave后续的同步就是增量同步,过程如下图

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

主从优化

  • 在master中配置repl-diskless-sync yes启用无磁盘复制,就是RDB不写入I/O流中,直接写入网络中传输给slave

全量同步时,正常将RDB先写入磁盘,然后通过网络传给salve,这个配置就是直接写入网络流中,少了一次IO写磁盘操作

  • Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘I/O
  • 适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
  • 限制一个master上的slave节点数量,如果slave实在太多,可以采用"主-从-从"的链式结构,减少master压力,如下图
    即从1节点作为从2节点的主节点,这样从1节点还是可以去读

哨兵模式

Redis提供了哨兵机制来实现主从集群的自动故障恢复,哨兵结构图如下:

1.哨兵集群搭建

首先创建三个目录,然后生成三个sentinel.conf文件,这三个目录是不同sentinel运行的目录,文件配置如下图,然后把端口改成三各不一样的,把配置文件分别放入三个目录中:

先启动我们的sentinel,三个都启动

这样的话snetinel集群就已经开始检测我们的redis主从集群了

这时可以让master宕机(手动关闭),发现有一个slave变成了master,原先的master重启后变成了slave

2.哨兵作用

  • 1.监控:Sentinel会不断地检查master和slave是否按期工作。
  • 2.自动故障恢复:如果master故障,Sentinel会将一个slave提升为master,当故障实例恢复后也以新的master为主。
  • 3.通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新的消息推送给Redis的客户端。

3.服务状态监控

Sentinel基于心跳机制检测服务状态,每隔一秒向集群的每个实例发送ping命令

  • 1.主观下线:如果某个sentinel节点发现某个实例未在规定时间响应,则认为该实例主观下线。
  • 2.客观下线:若规定指定数量(quorum)的sentinel都认为该实例主观下线,则认为该实例客观下线,quorum的值最好超过Sentinel实例数量的一半。
    只有sentinel认为redis主从的master客观下线后,才会选举新的master节点

4.选举新master依据

一旦发现master故障,sentinel需要在slave中选择一个作为新的master,选择依据如下:

  • 1.首先判断slave与master节点断开时间长短,如果超过指定值(down-after-milliseconds*10)则会排除该slave。(先排除数据过旧节点)

  • 2.然后判断slave的slave-priority值,越小优先级越高,如果是0则用不参加选举。(默认都是1)

  • 3.如果slave-priority值一样,则判断slave的offest值,越大说明数据越新,优先级越高。

  • 4.最后是判断slave节点的运行id,越小优先级越高。

5.故障转移

当选中了其中一个slave为新的master后,故障转移步骤如下图:

sentinel给备选的slave1发送slaveof no one命令,让该节点成为master

sentinel给所有其它slave发送slaveof 192.168.150.101 7002命令,让这些slave成为新master的从节点,开始从新的master上同步数据

最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点。

6.RedisTemplate连接哨兵模式

1.在pom文件中引入redis的starter依赖,如下图:

2.在配置文件applaction.yml中指定sentinel相关信息,如下图:

3.配置主从读写分离,如下图:

ReadForm是读取策略的选择,我们选择的话就下面两种,符合我们主写从读的特征

主从和哨兵可以解决高可用,高并发读的问题,但是依然没有解决以下两个问题:

  • 1.海量数据存储问题。
  • 2.高并发写的问题。

分片集群

该集群方式和上面集群方式是互斥的,或者说包含,同时包含了主从和哨兵机制

但是一般很大的QPS才会用这种集群方式估计

Redis分片集群如下

每个master保存不同数据;master之间通过ping检测批次的健康状态;(这里就不需要哨兵,master之间的ping代替了sentinel,这样的话如果多个master认为主观下线,也会客观下线,然后将salve变为新的master)

每个master可以有多个salve从节点

客户端请求可以访问集群任意节点,最终都会被准发到正确节点。

1.分片集群搭建

创建六个目录,配置文件如下图,端口号要设置成六个不同的,然后分别拷贝到六个目录中

将六个Redis都启动,此时这六个实例没有任何联系,我么要创建集群,如下图

结果图,代表分片集群启动成功

通过该命令查询集群信息

2.散列插槽

插槽的作用规定了,每个key存储到哪一个redis(插槽)

Redis会把0~16383共16384个插槽(hash slot)映射到每个master上,查看集群信息时就能看到,如下图:

数据key不是与节点绑定,而是与插槽绑定。Redis会根据key的有效部分通过CRC16算法得到一个hash值,然后会16384取余,得到的结果就是slot值

有效部分分两种情况:

  • 1.key中包含"{",且"{}"中至少包含1个字符,"{}"中的部分是有效部分。
  • 2.key中不包含"{}",整个key都是有效部分。

演示以证

{a}和a都到了7003中,这里是在7001中操作本来,如果你set a 1

key为1,这样的话重定向到了我们的7003(插槽在7003)

如何将一类数据固定保存在一个redis的master

应为你重定向也要消耗网络资源

3.集群伸缩

集群伸缩即向分片集群中新增删除节点

在集群中增加一个master,并为其分配插槽。

首先启动一个新的Redis实例,配置文件中设置其端口为7004,执行如下add-node命令将该实例作为master放入集群中,如下图

此时的7004还没有分配插槽,需要我们手动分配

查询集群节点状态可以看到:

使用reshard命令将7001中的部分插槽分配给7004:

分配7001所在集群的散列插槽

指定接收3000个散列插槽,指定7004的节点id未接收散列插槽的节点:

这里source node #1是散列插槽的来源节点id

下一个填入done代表执行,也可以填另一个节点id,相当于接收多个节点的散列插槽

4.故障转移

集群最开始各个节点状态如下图:

当让7002master停机后,发现7002的从节点8003从slave变成了master,如下图:

当再次启动7002后发现,7002为slave,说明Redis的分片集群有自动的故障恢复功能,如下图

5.手动故障转移

此时我们有一个新的Redis,该Redis性能比较好,需要把原来的master替换掉,这种场景就可以使用本小结的命令来执行,让其成为master的slave,然后把master替换掉。

在slave上执行cluster failover命令手动该slave对应的master宕机,让salve变为master,实现无感知的数据迁移,执行流程如下图:

手动的failover支持三种模式:

  • 1.缺省:默认的流程,如图1~6步。

  • 2.force:省略了对offset的一致性校验。

  • 3.takeover:直接执行第5步,忽略数据一致性、忽略master状态和其它master的意见。

举例:7002此时是slave,如下图:

当执行failover命令后,7002对应的8003master变为slave,7002则变为master,如下图:

6.RedisTemplate访问分片集群

  • 1.引入Redis的starter依赖。

  • 2.配置分片集群配置。

  • 3.配置读写分离。

  • 以上操作中,只有配置分片集群配置与哨兵机制不同,其他完全一致,配置如下图:

相关推荐
jstart千语3 小时前
【Redis】分布式锁的实现
数据库·redis·分布式
亚林瓜子3 小时前
AWS EC2源代码安装valkey命令行客户端
redis·云计算·aws·cli·valkey
?abc!4 小时前
缓存(5):常见 缓存数据淘汰算法/缓存清空策略
java·算法·缓存
专注代码七年5 小时前
在Windows 境下,将Redis和Nginx注册为服务。
windows·redis·nginx
智_永无止境5 小时前
Redis 8.0携新功能,重新开源
数据库·redis·开源
fengchengwu20125 小时前
langchain4j集成QWen、Redis聊天记忆持久化
redis·langchain·qwen·聊天记忆持久化
掘金-我是哪吒7 小时前
分布式微服务系统架构第125集:AI大模型
分布式
言小乔.7 小时前
202534 | KafKa简介+应用场景+集群搭建+快速入门
分布式·kafka
悟空打码8 小时前
MyBatis源码解读5(3.1、缓存简介)
缓存·mybatis
杨不易呀8 小时前
Java面试高阶篇:Spring Boot+Quarkus+Redis高并发架构设计与性能优化实战
spring boot·redis·高并发·分布式锁·java面试·quarkus