Pasted image 20240705101050
Pasted image 20240705101342

Redis-使用场景

Pasted image 20240705101855

如果发生了缓存穿透、击穿、雪崩,该如何解决?

缓存穿透

查询一个不存在的数据,mysql查询不到数据也不会直接写入缓存中,导致每次请求都查数据库

解决方案
  • 缓存空数据,查询返回的数据为空,但仍将这个空结果缓存到redis中
    • 优点:简单
    • 缺点:消耗内存,可能会发生不一致的问题
  • 布隆过滤器
    • 优点:内存占用较少,没有多余key
    • 缺点:实现复杂、存在误判
      Pasted image 20240705104520

布隆过滤器原理

Pasted image 20240705104945误判率:数组越小误判率就越大,数组越大误判率越小

缓存击穿

给某一个key设置了过期时间,当key过期的时候,恰好这个时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮
Pasted image 20240705112844

解决方案
  • 互斥锁

  • 逻辑过期

Pasted image 20240705113546

缓存雪崩

同一时间段大量的缓存key同时失效或者redis服务宕机,导致大量请求到达数据库,带来巨大压力
Pasted image 20240705114004

解决方案
  • 给不同的key的TTL添加随机值
  • 利用redis集群提高服务的可用性 (哨兵模式、集群模式)
  • 给缓存业务添加降级限流策略 (nignx 或者spring cloud gateway)
  • 给业务添加多级缓存 (Guava 或者 Caffeine)

mysql的数据如何与redis进行同步?(双写一致性问题)

一定一定要设置前提,先介绍自己的业务背景

双写一致:当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致

Pasted image 20240705132224

延时删除:需要等一会主节点将数据同步到从节点中
删除两次:因为先删除缓存再修改数据库由于线程并发的缘故,可能会导致数据不一致性,所以再删除一次缓存

解决方案

读写锁

共享锁(读锁):读不互斥、写互斥
排他锁(写锁):读写都互斥

特点:强一致性、性能低

这个项目的秒杀券的库存是放到了缓存中,要求实时的数据同步,为了保证数据的一致性,当时采用的是redisson提供的读写锁保证数据同步

那你来介绍一下redisson读写锁的这种方案
强一致性的,采用读写锁
1、共享锁(读锁,readLock):加锁之后、其他线程可以共享读操作
2、排他锁(独占锁,writeLock):阻塞其他线程读写读写操作

排他锁是如何保证读写、读读互斥的呢?
排他锁底层使用的是setnx,保证了同时只能有一个线程操作锁

你听说过延时双删吗?为什么不用它?
延时双删,如果是写操作,操作就是先把缓存中的数据先删除,然后更新数据库,最后再延时删除缓存中的数据,其实这个延时多久不好确定,在延时的过程中可能出现脏数据,并不能保证强一致性,所以没有采用

异步通知

Pasted image 20240705133355

回答:这个项目中blog数据是放到缓存中,因为实时性要求不高,所以采用的是异步的方案同步

那你来介绍一下异步的方案?
允许延时一致的业务,采用异步通知
1、采用MQ中间件,更新数据之后,通知缓存删除,需要保证MQ的可靠性
2、采用canal中间件,不需要修改业务代码,伪装为mysql的一个从节点,canal通过读取binlog数据更新缓存

基于Canal

Pasted image 20240705133503

redis持久化问题

redis作为缓存,数据的持久化是怎么做的呢?

RDB

全称 redis Database backup file(Redis 数据备份文件),也被叫做Redis数据快照。把内存中的所有数据都记录到磁盘中,当redis实例故障重启后,从磁盘读取快照文件,恢复数据
Pasted image 20240705142022

RDB的执行原理

bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据,完成fork后读取内存数据并写入RDB文件

Pasted image 20240705143131

fork采用copy-on-write技术:

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

AOF

AOF全称为Append Only File(追加文件),Redis处理的每一个写命令都会记录在AOF文件,可以看作是命令日志文件
Pasted image 20240705150924

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

AOF的命令记录频率也可以通过redis.conf文件来配
Pasted image 20240705153238

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

RDB和AOF的对比

如果对数据安全性较高,在实际开发中往往会结合两者来使用
Pasted image 20240705154340

参考回答

Pasted image 20240705154629

redis的过期策略

数据过期策略:redis对数据设置数据的有效时间,数据过期以后,就需要将数据从内存中删除掉。可以按照不同的规则进行删除,这种删除的规则就是数据的删除策略

惰性删除

设置key过期时间后,不去管它,当需要该key时,再检查其是否过期,如果过期,就删掉,反之,返回该key

  • 优点:对CPU优好,只有在使用key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查
  • 缺点:对内存不优好,如果一个key过期了,但是一直没有使用,那么该key就会一直存在内存中,内存永远不会释放

定期删除

每隔一段时间,对一些key进行检查,删除里面过期的key(从一定数量的数据库中取出一定数量的随机key进行检查,并删除其中的过期key)

两种模式

  • slow模式是定时任务,执行频率默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf的hz选项来调整这个次数

  • fast模式执行频率不固定,但两次间隔不超过2ms,每次耗时不超过1 ms

  • 优点:通过限制删除操作执行的时长和频率来减少删除操作对CPU的影响

  • 缺点:难以确认删除操作执行的时长和频率

redis过期策略:惰性删除+定期删除两种策略进行配合使用

参考问答

Pasted image 20240705162617

redis的数据淘汰策略

当redis内存不够用时,需要再向redis中添加新的key,那么redis就会按照某一中规则将内存中的数据删除掉,这种数据的删除规则称为内存的淘汰策略

八种数据淘汰策略

Pasted image 20240705164052

使用建议

Pasted image 20240705164355

数据库中有1000万数据,redis只能缓存20万数据,如何保证redis中的数据都是热点数据?
使用allkeys-lru数据淘汰策略,留下来的都是经常访问的热点数据

redis中的内存用完了会发生什么?
主要看数据淘汰策略是什么,如果是默认的配置,会直接报错

参考问答

Pasted image 20240705164850

分布式锁-使用场景

适用于集群环境下的定时任务、抢单、幂等性场景

redis实现分布式锁主要利用redis的setnx命令,setnx = set if not exists

redis实现分布式锁如何合理的控制锁的有效时长?
根据业务执行时间评估
给锁续期

Redisson实现分布式锁

  • 可重入:同一个线程执行业务可两次获取相同的锁(依据线程id)
  • 可超时续约:watchDog
  • 可重试:订阅机制

redisson实现分布式锁主从一致性:

RedLock(红锁):不能只在一个redis实例中创建锁,应该在多个redis实例中创建锁(n/2+1),避免在一个redis实例上加锁

问题
  • 实现复杂
  • 性能差
  • 运维繁琐

参考问题

Pasted image 20240705222146
但是可以采用redisson提供的红锁解决,但是这样的话性能较低,如果非要保证数据的强一致性的话,建议采用zookeeper实现的分布式锁

redis其他问题

redis集群有哪些方案

主从复制

Pasted image 20240706200340

主从同步原理
主从全量同步
- 

Pasted image 20240706200937

主从增量同步

(slave重启或者后期数据变化)
Pasted image 20240706201240

介绍一下redis的主从同步

单节点的redis并发能力是有限的,为了提高redis的并发能力,需要搭建redis集群,一般是一主多从,主节点负责写操作,从节点负责读操作

能说一下主从同步数据的流程吗?

Pasted image 20240706201905

参考问答

Pasted image 20240706201935

哨兵模式

实现主从集群的自动故障恢复

Pasted image 20240706203716

  • 监控:Sentinel(哨兵)会不断检查master和slave是否按照预期工作

基于心跳机制检测服务状态,每隔1秒向集群中的每个实例发送ping命令
- 主观下线:如果某个sentinel节点发现某实例未在规定时间内响应,则认为该实例主观下线
- 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过sentinel数量的一半

  • 自动故障恢复:如果master故障,sentinel会将一个slave提升为master。当故障恢复后以新的master为主

哨兵选主规则
- 首先判断从节点断开时间的长短,多超过指定值就排除该从节点
- 然后判断从节点的slave-priority,越小优先级越高
- 如果slave-priority相同,则判断slave节点的offset值,越大优先级越高
- 最后判断salve节点的运行id大小,越小优先级越高

  • 通知:当集群发生故障master节点转移时,会将最新的信息推送给redis的客户端

哨兵模式可能的其他问题-脑裂

Pasted image 20240706205237

由于网络原因,sentinel访问不了主节点,那么按照哨兵规则,他会将一个从节点升为主节点,这是集群中就会出现两个master节点
如果此时老的master网络恢复了,那么sentinel会将老的master强制转为slave

Pasted image 20240706205519
这时新的master会进行数据同步,老的master会将自己的数据清空
Pasted image 20240706205659
min-replicas-write 1 表示最少的slave节点为1(设置至少1个才能同步数据)
min-replicas-max-lag 5 表示数据复制和同步的延迟不超过5秒(5秒内slave必须同步master的数据,缩小数据丢失的时间)

如何保证redis的高并发高可用
哨兵模式:实现主从集群的自动故障恢复(监控、自动故障恢复、通知)

你们使用的redis是单点还是集群,哪种集群
主从(一主一从)+ 哨兵就可以了,一般单节点不超过10G内存,如果redis内存不足则可以给不同服务分配独立的redis主从节点

redis集群脑裂,该怎么解决?
集群脑裂是由于主节点和从节点和sentinel处于不同的网络分区,当由于网络问题,sentinel没有感知到主节点时,就会选取一个slave升为master,这时就会有两个master,就像大脑分裂了一样。这样会导致客户端还在老的master节点那里写入数据,而这些新数据无法同步。当网络恢复后,sentinel会将老的master节点将为slave节点,这是再从新的master同步数据,而原来的数据会被清空,导致数据丢失
解决办法:修改redis的配置,可以设置最少的从节点数量以及缩短主从同步数据同步的延迟时间

参考问答

Pasted image 20240706213408

分片集群

有两个问题没有解决
海量数据存储问题
高并发写的问题

Pasted image 20240706215106

分片集群的特征

Pasted image 20240706215200
数据读写的流程

redis分片集群中引入了哈希槽的概念,redis集群中有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放到那个槽,集群的每个节点负责一部分hash槽
Pasted image 20240706215731

分片集群有什么作用?

集群中有多个master,每个master保存不同数据(存储海量数据,高并发写)
每个master都可以有多个slave(高并发读)
master之间通过ping检测彼此健康状态
客户端请求可以访问集群任意节点,最终都会转发到正确节点(自动选择路由)

redis分片集群中数据是怎么存储和读取的?
redis分片集群中引入了哈希槽的概念,redis集群中有16384个哈希槽
将16384个插槽分配到不同的实例
读写数据:根据key的有效数据计算哈希值,对16384取余(有效部分,如果key前面有大括号,大括号中的内容就是有效部分,如果没有,key本身作为有效部分),余数做为插槽,寻找插槽所在的实例

参考问答

Pasted image 20240706221725

redis是单线程的,为什么还那么快

  • redis是纯内存操作,执行速度非常快
  • 采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题
  • 使用I/O多路复用模型,非阻塞IO

解释一下I/O多路复用模型

redis的性能瓶颈是网络延迟而不是执行速度,I/O多路复用模型主要就是实现了高效的网络请求

前置知识:
用户空间和内核空间
常见的IO模型:阻塞IO、非阻塞IO、IO多路复用
redis网络模型

用户空间和内核空间

Pasted image 20240707102639

阻塞IO

Pasted image 20240707103012

非阻塞IO

Pasted image 20240707103110

IO多路复用

Pasted image 20240707103623

具体的监听方式
Pasted image 20240707103916

redis网络模型

redis支持不同的多路复用实现,并将这些实现进行封装,提供了统一的高性能事件库
Pasted image 20240707104606

多线程
Pasted image 20240707104802

能解释一下IO多路复用吗?

1、指利用单线程同时监听多个socket,并在某个socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的IO多路复用都是采用的epoll模式实现,他会在通知用户进程Socket就绪的同时,把已就绪的socket写入用户空间,不需要挨个遍历socket来判断是否就绪,提升了性能

2、redis网络模型
使用IO多路复用结合事件的处理器来应对多个socket请求
连接应答处理器
命令回复处理器,在redis6.0之后,为了提升更好的性能,使用了多线程来回复处理事件
命令请求处理器,在redis6.0之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程

参考问答

Pasted image 20240707105914