redis(面试题)

redis的使用场景?

缓存(穿透,击穿,雪崩,双写一致,持久化,数据过期策略,数据淘汰策略)

分布式锁

什么是缓存穿透,怎么解决?

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

解决方案一:缓存空数组(优点:简单,缺点:消耗内存,可能会发生数据不一致的问题)

解决方案二:布隆过滤器(优点:内存占用较少,没有多余key,缺点:实现复杂,存在误判)

什么是缓存击穿?

缓存击穿:给某一个key设置了过去时间,当key过期的时候,恰当这时间点对这个key有大量的并发请求过来,这些并发请求可能会瞬间把DB压垮

解决方案一:互斥锁(强一致性,性能差)

解决方案二:逻辑过期(高可用,性能优,不能保证数据的绝对一致)

什么是缓存雪崩?

缓存雪崩:是指在同一时间段大量的缓存key同时失效或者redis服务宕机,导致大量请求到达数据库,带来巨大压力。

解决方案:

  • 给不同的key的TTL添加随机值
  • 利用redis集群提高服务的可用性(哨兵模式,集群模式
  • 给缓存业务添加降级限流策略(nginx,Spring Cloud Gateway
  • 给业务添加多级缓存(Guava或者Caffeine

降级可作为系统的保底策略,适用于穿透,击穿,雪崩 

什么是双写一致性?

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

解决方案:

  • 允许延时一致性的业务,采用异步同步
  1. 利用MQ中间件,更新数据之后,通知缓存删除
  2. 利用cancal中间件,不需要修改业务代码,伪装mysql的一个从节点,canal通过读取binlog数据更新缓存
  • 强一致性,采用redisson提供的读写锁
  1. 共享锁:读锁readLock,加锁之后,其他线程可以共享读操作
  2. 排他锁:独占锁writeLock也叫,加锁之后,阻塞其他线程读写操作

redis的持久化是怎么做的?

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

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

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

RDB与AOF对比

RDBAOF
持久化方式定时对整个内存做快照记录每一次执行的命令
数据完整性不完整,两次备份之间会丢失相对完整,取决于刷盘策略
文件大小会有压缩,文件体积小记录命令,文件体积很大
宕机恢复速度很快
数据恢复优先级低,因为数据完整性不如AOF高,因为数据完整性更高
系统资源占用高,大量CPU和内存消耗低,主要是磁盘IO资源但AOF重写时会占用大量CPU和内存资源
使用场景可以容忍数分钟的数据丢失,追求更快的启动速对数据安全性要求较高常见

数据过期策略

惰性删除:设置改key过期时间后,我们不去管他,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key

优点:对cpu友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过去检查

缺点:对内存不友好,如果一个key已经过期,但是在一直没有使用,那么该key就会一直存在内存中,内存永远不会释放

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

定期清理的两种模式

  • SLOW模式是定时任务,执行频率默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf的hz选项来调这个次数
  • FAST模式执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms、

优点:可以通过限制删除操作执行的时长和频率来减少删除操作对CPU的影响,另外定期删除,也能有效释放过期键占用的内存

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

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

数据淘汰策略

 数据淘汰策略:当Redis中内存不够用时。此时在向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除掉,这种数据的删除规则被称之为内存淘汰策略。

  • noeviction:不淘汰任何key,但是内存满时不允许写入新数据,默认就是这种策略
  • volatile-ttl:对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰
  • allkeys-random:对全体key,进行随机淘汰。
  • volatile-random:对设置了TTL的key,随机进行淘汰。
  • allkeys-lru:对全体key,基于LRU算法进行淘汰
  • volatile-lru:对设置了TTL的key,基于LRU算法进行淘汰
  • allkeys-lfu:对全体key,基于LFU算法进行淘汰
  • volatile-lfu:对设置了TTL的key,基于LFU算法进行淘汰

LRU:最少最近使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高

LFU:最少频率使用,会统计每个key的访问频率,值越小淘汰优先级越高

平时在开发过程中用的比较多的就是allkeys-lru

redis分布式锁

redis实现分布式锁主要利用redis的setnx命令,setnx时SET if not exists(如果不存在,则SET)的简写。

redisson实现分布式锁-执行流程

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

RedLock(红锁):不能旨在一个redis实例上创建锁,应该在多个redis实例上创建锁(n / 2 + 1),避免在一个redis实例上加锁,缺点:实现复杂,性能差,运维繁琐(不推荐使用

Redisson实现分布式锁如何合理的控制锁的有效时长?

在redisson的分布式锁中,提供了一个WatchDog(看门狗),一个线程获取锁成功以后WatchDog会给持有锁的线程续期 (默认是每隔10秒续期一次)

Redisson的这个锁,可以重入吗?

可以重入,多个锁重入需要判断是否是当前线程,在redis中进行存储的时候使用的hash结构来存储线程信息和重入的次数

Redisson锁能解决主从数据一致的问题吗

不能解决,但是可以使用redisson提供的红锁来解决,但是这样的话,性能就太低了,如果业务中非要保证数据的强一致性,建议采用zookeeper实现的分布式锁

redis集群有哪些方案?

主从复制

单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。一般都是一主多从,主节点负责写数据,从节点负责读数据

主从同步数据的流程:

全量同步:

  1. 从节点请求主节点同步数据 (replication id、offset)
  2. 主节点判断是否是第一次请求,是第一次就与从节点同步版本信息 (replication id和offset)
  3. 主节点执行bgsave,生成rdb文件后,发送给从节点去执行
  4. 在rdb生成执行期间,主节点会以命令的方式记录到缓冲区(一个日志文件)
  5. 把生成之后的命令日志文件发送给从节点进行同步增量同步

增量同步:

  1. 从节点请求主节点同步数据,主节点判断不是第一次请求,不是第一次就获取从节点的offset值
  2. 主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步

哨兵模式

redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。

监控:Sentinel会不断检查master和slave是否按预期工作

自动故障恢复:如果master故障,Sentinel会将slave提升master,当故障实例恢复后也以新的master为主

通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端

服务状态监控

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

  • 主观下线:如果sentinel节点发现某个实例围在规定时间响应,则以为该实例主管下线
  • 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线,quorum值最好是超过了Sentinel实例数量的一半

哨兵选主规则

  • 首先判断主与从节点断开的时间长短,如超过指定值就排从节点
  • 然后判断从节点slave-priority的值,越小优先级越高
  • 如果slave-proity一样,则判断slave节点的offset值,越大优先级越高
  • 最好是判断slave节点的允许id大小,越小优先级越高
怎么保证Redis的高并发高可用

哨兵模式:实现主从集群的自动故障恢复(监控、自动故障恢复、通知)

你们使用redis是单点还是集群,哪种集群

主从(1主1从)+哨兵就可以了。单节点不超过10G内存,如Redis内存不足则可以给不同服务分配独立的Redis主从节点

redis集群脑裂,该怎么解决呢?

集群脑烈是由于主节点和从节点和sentinel外不同的网络分区,使得sentinel没有能够心跳感知到主节点,所以通过选举的方式提升了一个从节点为主,这样就存在了两个master,就像大脑分裂了一样,这样会导致客户端还在老的主节点那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会将老的主节点降为从节点,这时再从新master同步数据,就会导致数据丢失

解决:我们可以修改redis的配置,可以设置最少的从节点数量以及缩短主从数据同步的延迟时间,达不到要求就拒绝请求就可以避免大量的数据丢失

分片集群结构

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

  • 海量数据存储问题
  • 高并发写的问题

使用分片集群可以解决上述问题,分片集群特征:(redis的分片集群有什么作用

  • 集群中有多个master,每个master保存不同数据
  • 每个master都可以有多个slave节点
  • master之前通过ping检测彼此健康状态
  • 客户端请求可以访问集群任意节点,最终都会被转发到正确节点

Redis分片集群中数据是怎么存储和读取的?

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

Redis是单线程的,但是为什么还是那么快呢?

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

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

redis是纯内存操作,执行速度非常快,它的性能瓶颈期是网络延迟而不是执行速度,I/O多路复用模型主要就是实现了高效的网络请求

  • 用户空间和内核空间
  • 常见的IO模型
  1. 阻塞IO(Blocking IO)
  2. 非阻塞IO(Nonblocking IO)
  3. IO多路复用(IO Multiplexing)
  • Redis网络模型

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

不过监听Socket的方式、通知的方式又有多种实现,常见的有:

  • select
  • poll
  • epoll

差异:

  • select和poll只会通知用户进程有Socket就绪,但不确定具体是哪个Socket,需要用户进程逐个遍历Socket来确认
  • epoll则会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间

Redis网络模型

Redis通过IO多路复用来提高网络性能,并且支持各种不同的多路复用实现,并且将这些实现进行封装,提供了统一的高性能事件库

  • 连接应答处理器
  • 命令回复处理器,在Redis6.0之后,为了提升更好的性能,使用了多线程来处理回复事件
  • 命令请求处理器,在Redis6.0之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值