Spring Cloud分布式缓存

目录

单点Redis

Redis数据持久化

RDB持久化

bgsave细节

RDB的缺点

AOF持久化

AOF的问题

RDB与AOF对比

搭建Redis主从架构

数据同步原理

全量同步

增量同步

主从同步优化

Redis哨兵

集群检测

选举主节点

故障转移

搭建哨兵集群

RedisTemplate的哨兵模式


单点Redis

单点Redis存在如下问题:

  • Redis是内存存储,服务重启可能会造成数据丢失。
  • 并发问题,虽然是内存存储,并发能力很强,但在单节点下,不适用于高并发的场景。
  • 故障恢复问题,Redis服务宕机,导致某些服务不可用。需要一种自动恢复的手段。
  • 存储能力问题,Redis基于内存,单节点的存储数量难以满足海量数据需求。

对应的解决方案:

  • 数据丢失问题:实现Redis数据持久化
  • 并发能力问题:搭建主从集群,实现读写分离
  • 故障恢复问题:利用Redis哨兵,实现健康和自动恢复
  • 存储能力问题:搭建分片集群,利用插槽机制实现动态扩容

Redis数据持久化

一共有两种持久化方式:RDB与AOF

RDB持久化

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

执行命令为

save

该命令由Redis主进程去执行,但由于Redis是单线程的,因此在持久化期间,其他所有命令都会被阻塞。一般不推荐这种方式。而推荐下面这个命令

bgsave

开启子进程执行RDB,不影响主进程。

Redis在主动停机前,会自动执行一次RDB。

Spring Cloud分布式缓存_第1张图片

Spring Cloud分布式缓存_第2张图片

但是在Redis内部数据比较多的时候,RDB时间可能会很久,在save期间,如果服务宕机,仍然可能导致数据丢失。因此我们可以在配置文件中修改RDB触发机制。

Spring Cloud分布式缓存_第3张图片

需要注意的是,以上save实际上都是bgsave,如果是 save "",则代表禁用RDB。

其他配置如下

Spring Cloud分布式缓存_第4张图片

Spring Cloud分布式缓存_第5张图片

修改RDB触发条件为5秒内至少有一个key被修改后,重启Redis服务,再进行一个添加操作观察redis服务器,会自动进行RDB。

Spring Cloud分布式缓存_第6张图片

bgsave细节

bgsave是开启一个子进程去对数据进行持久化操作,虽然实现了异步持久化,但是在fork主进程得到子进程期间是一个阻塞式的操作,为了减少阻塞时间,fork底层实现如下。

Spring Cloud分布式缓存_第7张图片

主进程是无法直接对物理内存进行操作的,开启主进程时,操作系统会对主进程分配一个虚拟内存,并维护页表,而页表中记录了虚拟内存与物理内存的映射关系,主进程开启子进程过程中仅仅是拷贝了一个页表,并不是拷贝了内存中的数据给子进程去做持久化。因此,主进程和子进程实际上共享同一个内存。

共享同一个内存也存在一个缺点,就是在RDB过程中,主进程需要对数据进行修改。这样就造成了读写冲突。为此,fork底层采用了copy-on-write技术:

  • 当主进程执行读操作时,访问共享内存
  • 当主进程执行写操作时,则会拷贝一份数据,执行写操作。

Spring Cloud分布式缓存_第8张图片

修改数据前,将原有数据拷贝一份后再进行写操作,同时将主进程中的页表映射关系进行修改。

RDB的缺点

  1. RDB执行间隔时间长,两次RDB之间写入数据有丢失风险。
  2. fork子进程,压缩,写出RDB文件都比较耗时。

AOF持久化

Redis处理的每一个写命令都会记录在AOF文件中,可以看做命令日志文件

Spring Cloud分布式缓存_第9张图片

当服务重启后,会从AOF文件中将所有命令再执行一边。而AOF默认是关闭的,需要修改redis.conf配置文件来开启AOF

Spring Cloud分布式缓存_第10张图片

三种刷盘机制对比 

配置项

刷盘时机

优点

缺点

Always

同步

可靠性高,几乎不丢失数据

性能影响较大

everysec

每秒刷盘

性能适中

最多丢失1秒数据

no

操作系统控制

性能最好

可靠性较差,可能丢失大量数据

开启AOF功能后,执行一次写操作,观察AOF文件

Spring Cloud分布式缓存_第11张图片

Spring Cloud分布式缓存_第12张图片

重启Redis服务,会进行一次DB加载

Spring Cloud分布式缓存_第13张图片

AOF的问题

AOF会记录所有的写操作,但是对于同一个key,记录多次set操作是无意义的,只需要最后一次的set值就满足了,如果执行了delete操作,那么之前的set操作也无意义。因此可以通过执行bgrewriteaof命令,对AOF文件进行重写。可以通过修改配置文件来控制重写时机

Spring Cloud分布式缓存_第14张图片

RDB与AOF对比

RDB

AOF

持久化方式

定时对整个内存做快照

记录每一次执行的命令

数据完整性

不完整,两次备份之间会丢失

相对完整,取决于刷盘策略

文件大小

会有压缩,文件体积小

记录命令,文件体积很大

宕机恢复速度

很快

数据恢复优先级

低,因为数据完整性不如AOF

高,因为数据完整性更高

系统资源占用

高,大量CPU和内存消耗

低,主要是磁盘IO资源

但AOF重写时会占用大量CPU和内存资源

使用场景

可以容忍数分钟的数据丢失,追求更快的启动速度

对数据安全性要求较高常见

搭建Redis主从架构

Reids搭建集群主要是为了实现读写分离,由于大多数使用Redis做缓存,因此是读多写少,也就是说,主节点去实现写操作,从节点去实现读操作。

Spring Cloud分布式缓存_第15张图片

接下来我们在同一台虚拟机上创建3个Redis实例,实现主从集群

#创建3个目录,分别存放不同启动端口的Redis实例
cd /tmp
mkdir 7001 7002 7003
#将redis中的redis.conf文件拷贝到这三个文件当中
# 方式一:逐个拷贝
cp redis-6.2.4/redis.conf 7001
cp redis-6.2.4/redis.conf 7002
cp redis-6.2.4/redis.conf 7003
# 方式二:管道组合命令,一键拷贝
echo 7001 7002 7003 | xargs -t -n 1 cp redis-6.2.4/redis.conf

# 修改配置文件中的启动端口以及文件保存位置
sed -i -e 's/6379/7001/g' -e 's/dir .\//dir \/tmp\/7001\//g' 7001/redis.conf
sed -i -e 's/6379/7002/g' -e 's/dir .\//dir \/tmp\/7002\//g' 7002/redis.conf
sed -i -e 's/6379/7003/g' -e 's/dir .\//dir \/tmp\/7003\//g' 7003/redis.conf

#修改每个实例的声明IP
# 逐一执行
sed -i '1a replica-announce-ip 192.168.150.101' 7001/redis.conf
sed -i '1a replica-announce-ip 192.168.150.101' 7002/redis.conf
sed -i '1a replica-announce-ip 192.168.150.101' 7003/redis.conf
# 或者一键修改
printf '%s\n' 7001 7002 7003 | xargs -I{} -t sed -i '1a replica-announce-ip 192.168.150.101' {}/redis.conf

#启动
redis-server 7001/redis.conf 
redis-server 7002/redis.conf 
redis-server 7003/redis.conf 

接下来搭建主从关系:

临时方式(重启失效)

客户短连接redis服务后,执行slaveof方法

slaveof 主节点ip 主节点端口

永久方式:修改配置文件添加如下配置

slaveof 主节点ip 主节点端口

Spring Cloud分布式缓存_第16张图片

连接后主节点会打印节点同步信息。将7002、7003将7001作为主节点后,输入如下命令查看集群信息

info replication

Spring Cloud分布式缓存_第17张图片

测试是否可以同步信息,在7001加入数据,在7002查询数据(在从节点无法写入数据)

Spring Cloud分布式缓存_第18张图片

数据同步原理

全量同步

主从第一次同步也叫全量同步,具体流程如下

Spring Cloud分布式缓存_第19张图片

解释:当从节点发起数据同步请求时,主节点会判断该节点是否是第一次进行数据同步,如果是第一次,主节点会返回自己数据的版本信息给从节点保存,同时执行一个bgsave操作,去生成RDB文件后发送给从节点,在生成RDB文件期间会将所有的写操作保存在repl_baklog命令缓冲区。从节点接收到RDB文件后,会清空自身数据后加载RDB文件,加载完成后,主节点会将缓冲区的所有命令发送给从节点去执行,从而保证主从信息保持一致。

master如何判断slave是不是第一次同步数据?

Replication Id:简称replid,是数据集的标记,id一致说明是同一数据集,每一个master都存在唯一的replid,而slave会继承master的id用来识别主从节点属于同一数据集。

offset:偏移量。随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset,如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。

slave做数据同步时,必须向master声明自己的replication id(用来判断是否使用同一个数据集)和offset(用来判断同一个数据集下的同步进度),master才会知道有哪些数据需要同步。

因此第一阶段就变成如下流程

Spring Cloud分布式缓存_第20张图片

查看Redis服务器打印信息

Spring Cloud分布式缓存_第21张图片

Spring Cloud分布式缓存_第22张图片

Spring Cloud分布式缓存_第23张图片

增量同步

当slave宕机重启后,再次与主节点连接时,进行的是增量同步(局部同步)

Spring Cloud分布式缓存_第24张图片

解释:slave发送自己的数据集id和偏移量信息给主节点,主节点判断不是第一次连接后,就进行增量同步。将repl_baklog命令缓冲区获取自身偏移量与主节点记录的偏移量之间的数据。

Spring Cloud分布式缓存_第25张图片

由于repl_baklog的文件大小固定,当写满后,会覆盖最早的数据(可以理解为环形数组)。如果slave断开过久,导致未备份的数据被覆盖,则无法基于repl_baklog做增量同步,只能进行全量同步。

主从同步优化

由于全量同步耗时比较久,因此我们要尽可能的减少Redis进行全量同步的次数

  • 在主节点的配置文件中配置repl-dishless-sync yes启用无磁盘复制,避免全量同步时的磁盘IO。简单来说就是在写RDB文件时,不写入磁盘,而是通过网络IO流直接写给从节点(适用于网络带宽快的场景)
  • Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO(数据少,那么IO流传输数据就少)
  • 适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
  • 限制一个master上的slave节点数量,如果实在是太多slave,则可以采用主-从-从链式结构,减少master压力

Spring Cloud分布式缓存_第26张图片

Redis哨兵

Redis哨兵(Sentinel)机制来实现主从集群的自动故障恢复,结构如下:

Spring Cloud分布式缓存_第27张图片

Redis哨兵作用如下:

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

集群检测

Redis哨兵通常集群搭建,基于心跳检测来监控所有节点的状态,每隔一秒向集群每个节点发送ping,如果超过时间没有接收到响应,则认定为主观下线,如果Redis哨兵集群超过指定数量(建议是节点数量的一半)的节点都没有接收到响应,则认定为客观下线(真的下线了),移除下线节点,如果是主节点宕机,需要及时选举新的主节点。其次就是通知java客户端,告知客户端去访问哪个节点。

选举主节点

当主节点宕机后,需要在slave中选举一个主节点,选举依据:

  • 首先会判断slave结点与master结点断开时间长短,如果超过指定值(down-after-milliseconds),直接排除该slave结点(与主节点断开时间过长,缺失数据过多,不适合选举主节点)
  • 判断slave节点的slave-priority值(配置文件中配置。默认一样),越小优先级越高,如果为0则不参加选举
  • 如果slave-priority一样,则判断slave结点的offset值,越大说明数据越新,优先级越高
  • 最后判断运行id(启动顺序),越小优先级越高

故障转移

当主节点(7001)宕机后,选举slave(7002)为主节点后,故障转移的步骤如下:

  • sentinel给备选节点发送slaveof no one命令,让该节点成为新的master
  • sentinel给其他slave节点发送slaveof 新的主节点ip 新的主节点端口 命令,让其他slave节点成为新的主节点的从节点,开始从新的主节点上同步数据。
  • 将原来的主节点标记为slave(实际上是在配置文件中添加了slaveof命令),再重启后自动成为新的主节点的从节点

Spring Cloud分布式缓存_第28张图片

搭建哨兵集群

我们还是在同一台虚拟机上去搭建哨兵集群。具体命令如下

# 进入/tmp目录
cd /tmp
# 创建目录
mkdir s1 s2 s3

# 添加配置文件
vi s1/sentinel.conf

配置如下信息

port 27001 #端口后面两个设置为27002 27003
sentinel announce-ip 192.168.150.101 # 声明Sentinel的IP地址
sentinel monitor 集群名称(自定义) 192.168.150.101 7001 2 # 监控主节点的地址 2代表指定的数量来决定节点主观下线
sentinel down-after-milliseconds 集群名称 5000 # 指定slave与master断开超过时间失去选举权
sentinel failover-timeout mymaster 60000 # slave故障恢复的时间
dir "/tmp/s1" # 工作目录

接着配置s2、s3目录下的配置文件

# 方式一:逐个拷贝
cp s1/sentinel.conf s2
cp s1/sentinel.conf s3
# 方式二:管道组合命令,一键拷贝
echo s2 s3 | xargs -t -n 1 cp s1/sentinel.conf

# 修改s2、s3两个文件夹内的配置文件,将端口分别修改为27002、27003
sed -i -e 's/27001/27002/g' -e 's/s1/s2/g' s2/sentinel.conf
sed -i -e 's/27001/27003/g' -e 's/s1/s3/g' s3/sentinel.conf

启动

# 第1个
redis-sentinel s1/sentinel.conf
# 第2个
redis-sentinel s2/sentinel.conf
# 第3个
redis-sentinel s3/sentinel.conf

接下来主动断开7001的服务,模拟主节点宕机。观察哨兵集群打印的消息

Spring Cloud分布式缓存_第29张图片

去查看7003的打印信息

接着查看sentinel信息

Spring Cloud分布式缓存_第30张图片

观察7002节点信息

Spring Cloud分布式缓存_第31张图片

重启7001节点,观察主节点信息

Spring Cloud分布式缓存_第32张图片

RedisTemplate的哨兵模式

将资料中的redis-demo文件使用IDEA打开

在pom文件中引入redis的starter依赖


	org.springframework.boot
	spring-boot-starter-data-redis

在配置文件中application.yml中指定sentinel相关信息

spring:
  redis:
    sentinel:
      master: mymaster #指定集群名称
      nodes: # 配置sentinel集群信息
        - 192.168.116.131:27001
        - 192.168.116.131:27002
        - 192.168.116.131:27003

配置读写分离

@Bean
public LettuceClientConfigurationBuilderCustomizer configurationBuilderCustomizer(){
    return configBuilder ->configBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}

这里的ReadFrom是配置Redis的读取策略,是一个枚举类,包括如下选择:

  • MASTER:从主节点读取
  • MASTER_PREFERRED:优先从master节点读取,master不可用才读取replica
  • REPLICA:从slave节点读取
  • REPLICA_PREFERRED:优先从slave节点读取,所有的slave都不可用时才读取master

确保redis集群中存在数据后,启动并访问get/{key}(key为redis中的key名称),并观察控制台

Spring Cloud分布式缓存_第33张图片

Spring Cloud分布式缓存_第34张图片

Spring Cloud分布式缓存_第35张图片

Spring Cloud分布式缓存_第36张图片

Spring Cloud分布式缓存_第37张图片

Spring Cloud分布式缓存_第38张图片

接着执行一次set操作,访问/set/{key}/{value}接口

Spring Cloud分布式缓存_第39张图片

接下来测试故障转移,将7003宕机,观察sentinel控制台

Spring Cloud分布式缓存_第40张图片

可以看到又将7001作为主节点了。接下来看到Java客户端又输出很多打印信息

Spring Cloud分布式缓存_第41张图片

Spring Cloud分布式缓存_第42张图片

Spring Cloud分布式缓存_第43张图片

可以看到,Java客户端只需要连接哨兵集群,就可以动态的获取到主节点信息与从节点信息。

你可能感兴趣的:(spring,cloud,redis)