Redis相关面试题
针对Redis的考察,大致分为使用场景和其他面试题,诸如redis集群的搭建(主从,哨兵,集群),事务,Redis为什么怎么快等等

首先我们先介绍一下使用场景的部分
一.缓存穿透
介绍:缓存穿透指的是,缓存中没有该数据,请求直接穿透缓存去访问数据库,浪费了缓存的作用
解决方案:
1. 缓存空数据,查询返回的数据结果为空,仍然把这个空结果进行缓存
优点:简单
缺点:消耗内存,可能发生不一致的问题
2. 布隆过滤器
实现方案:Redisson Guava
优点:内存占用较少,没有多余Key
缺点:实现复杂,存在误判
二. 缓存击穿
给一个key设置了过期时间,当key过期的时候,恰好这个时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮

解决方案:互斥锁(分布式锁),逻辑过期
![[图片]
[图片]](https://i-blog.csdnimg.cn/direct/5afeabe24f83488cb2e054704530a116.png)
互斥锁方案->数据的强一致性 具体实现逻辑:在缓存失效后,只有一个线程(加锁)可以查库回写缓存,其他线程等待或重试,避免并发穿透。
逻辑过期方案->高可用性 具体实现逻辑:设置逻辑时间戳过期,先返回旧值,后天异步更新数据。
三. 缓存雪崩
缓存雪崩是指同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到数据库,带来巨大的压力。
1.大量key过期
解决方案:给不同的key的TTL(time to live)设置随机的过期时间
2.Redis服务宕机
解决方案:利用Redis集群提高服务的可用性(哨兵模式,集群模式);给缓存业务添加降级限策略(nginx,gateway或者其他中间件);给业务添加多级缓存;
4.Redis如何实现双写一致性
双写一致:当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致。
场景:Redis作为缓存,mysql的数据如何与Redis进行同步?
根据业务背景进行回答:一致性要求高;允许延迟一致;
读操作:缓存命中,直接返回;缓存未命中查询数据库,写入缓存,设定超时时间。
写操作:延迟双删
强一致性的情况:使用共享锁或者排他锁,具体代码实现一样采用Redisson框架
左:采用readlock,读锁(共享锁),加锁之后,其他线程可以共享读操作
右:writelock,也叫独占锁,加锁之后,阻塞其他线程的读写操作。
![[图片]
[图片]](https://i-blog.csdnimg.cn/direct/ce9bca04689043f797501f72176d2f20.png)

允许延迟一致:说白了就是最终一致
1.使用MQ,主要依赖MQ的可靠性

2.使用canal中间件
![[图片]
[图片]](https://i-blog.csdnimg.cn/direct/d059e19ddc1648fb932a5f4f5e3a32b5.png)
5.Redis是怎么实现持久化的
5.1 RDB
RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做数据快照。简单来说就是把内存中的所有数据都记录在磁盘中。当Redis实例故障后,从磁盘读取快照文件,恢复数据。
连接客户端执行RDB
Redis内部有触发RDB的机制,可以通过Redis.conf文件中找到。格式如下:
-- 900秒内,如果有一个key杯修改,执行bgsave
save 900 1
Save 300 10
Save 60 10000
RDB的实现原理
页表能够记录虚拟地址和物理地址的映射关系;
Redis 在生成 RDB 快照时会执行 BGSAVE,主进程通过 fork 创建子进程。fork 后父子进程共享同一份物理内存,通过页表映射实现。 操作系统会将共享内存页标记为 read-only。 如果父进程在期间修改数据,就会触发 Copy-On-Write,操作系统复制新的内存页供父进程修改,而子进程仍然读取旧数据。 子进程基于 fork 时刻的内存快照生成 RDB 文件,写入完成后通过 rename 原子替换旧的 RDB 文件
5.2 AOF
AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做事命令日志文件。
![[图片]
[图片]
[图片]](https://i-blog.csdnimg.cn/direct/6cb6bd5293f047f6bf534ff0a31238f3.png)


6.Redis的过期策略
6.1 惰性删除

6.2定期删除

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


LRU:最近最少使用。用当前时间减去最后一次访问时间,这个值越大淘汰优先级越高
LFU:最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高。
8.分布式锁
8.1 setnx命令。setnx是SET if not exits(不存在,则设置)
· 获取锁
– NX代表的是互斥,ex代表的是设置超时时间。
SET lock value NX EX 10
· 释放锁
– 删除锁即可
DEL key
8.2 Redisson框架

增加了看门狗机制,会根据你锁的过期时间的/3自动续期,默认是10秒,其他线程加锁失败后会进行while循环重试,且重试机制存在阈值。
![[图片]](https://i-blog.csdnimg.cn/direct/13e49c3ba1734f858a1d85b6dcd37c26.png)
lock.tryLock()传入的参数中,如果传入了如(10,30,TimeUnit,SECONDS);第二个参数则为锁的失效时间,此时看门狗机制会失效;加锁,设置过期时间等操作都是基于Lua脚本完成的,可以保证操作的原子性
Redisson实现的锁是可以重入的,哈希结构中的value会记录冲入的次数。
当设计到Redis主从集群的时候,若要实现主从一致性,可以根据实现业务的效果采取不同的方案。
要实现高可用性,使用Redis,即AP的情况

实现强一致性,采取CP的思想,使用zookeeper.
可以参考大佬写的这篇文章https://blog.csdn.net/m0_45406092/article/details/118185561
这篇博客写的是zk和Redis分布式锁的区别,同时介绍了Redis三种集群模式,包括了Redis哨兵模式脑裂问题,讲的不错。
9.Redis集群
9.1 主从复制
单节点Redis的并发能力是有限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。一般都是一主多从,主节点负责写数据,从节点负责读数据。
主从数据同步的原理
解释:关于步骤6中,Redis在生成RDB文件时会执行bgsave,主进程通过fork创建子进程。执行一系列操作后子进程基于fork时刻的内存快照生成RDB文件,写入完成后通过rename原子替换旧的RDB文件。
全量同步
在进行数据同步的时候,是怎么确定是否是第一次同步的呢?又是怎么确定同步的数据的一致性?
在这里需要介绍两个概念
1.Replication Id
是数据集的标记,id 一致则说明是同一数据集。每一个 master 都有唯一的 replid,slave 则会继承 master 节点的 replid
2.offset(偏移量)
偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave 完成同步时也会记录当前同步的 offset。如果 slave 的 offset 小于 master 的 offset,说明 slave 数据落后于 master,需要更新。
在 Redis 主从复制中,从节点发起同步时会发送 PSYNC replid offset。
如果是第一次同步,从节点会发送 PSYNC ? -1,主节点会执行 FULLRESYNC,生成 RDB 并进行全量步。
如果不是第一次同步,主节点会判断从节点携带的 replid 是否与当前主节点一致,同时判断 offset 是否仍然在 replication backlog buffer 中。
如果条件满足,则执行 CONTINUE 进行增量同步,只发送 offset 之后的命令;否则会执行全量同步。
Redis 通过 replid + offset 来保证主从数据的一致性。
增量同步:

redis为了保证高可用,主从复制过程是异步的,也就是说,当客户端向master写了一个数据,master提交完就给你飞吻了,而不管slaver是否已经同步完成数据,这样写数据和同步数据各玩各的,同步数据过程中不影响写数据,这样叫做高可用,但也带来了以下两个问题:
问题1,主从复制丢数据加粗样式
C1向master申请到了锁a,master在同步数据的过程中挂掉了,此时slaver还没有拿到锁a的数据,主备切换,slaver升级成master1,C2向master1申请锁a,申请成功,此时C1和C2都拿到锁,锁失效了。
问题2,脑裂
C1向master申请到了锁a,master网络出问题了,sentinel收不到master的心跳,于是将slaver选举成master1,但此时还有些C端能够访问到master,假如此时C2能够访问到master,但C3访问的却是master1,于是C2在master上能够申请到锁b,C3在master1上也能够申请到锁b
9.2 哨兵模式(实现Redis的高可用)
哨兵的作用
Redis的Sentinel的服务状态监控



Redis 如何解决脑裂?
可以回答:
Redis 在 Sentinel 模式下通过多种机制降低脑裂带来的影响。首先 Sentinel 使用 quorum 机制,只有达到指定数量的 Sentinel 同时认为 master 下线,才会触发故障转移,避免误判。其次 Sentinel 会进行 Leader 选举,只有被选举出来的 Leader Sentinel 才能执行 failover,避免多个 slave 同时被提升为 master。此外 Redis 还提供了写保护机制,通过配置 min-replicas-to-write 和 min-replicas-max-lag,当 master 发现没有足够的从节点或者复制延迟过大时,会拒绝写请求,从而减少脑裂情况下的数据丢失。
在这里要区分两个概念,Redis Sentinel和Spring Cloud Alibaba Sentienl不是同一个组件。
Redis Sentinel 是 Redis 官方提供的高可用解决方案,主要用于监控 Redis 主从节点并在主节点故障时自动进行故障转移。而 Spring Cloud Alibaba Sentinel 是阿里开源的微服务流量治理组件,主要提供限流、熔断和降级功能,用于保护微服务系统的稳定性。两者只是名字相同,但解决的问题完全不同。
9.3 分片集群
解决海量数据存储问题,高并发写的问题,分片集群结构

分片集群的数据读写原理
如果业务需要将同一类数据固定保存在同一个Redis实例?
可以将这一类数据使用相同的有效部分,例如key都以{typeId}为前缀。
Redis集群添加和删除节点
https://blog.csdn.net/Howinfun/article/details/81938161?ops_request_misc=&request_id=&biz_id=102&utm_term=Redis%E9%9B%86%E7%BE%A4%E6%B7%BB%E5%8A%A0%E4%B8%80%E4%B8%AA%E6%96%B0%E8%8A%82%E7%82%B9&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-0-81938161.142%5Ev102%5Epc_search_result_base8&spm=1018.2226.3001.4187
10.Redis是单线程的,为什么还这么快?
· Redis是纯内存操作,执行速度非常快。
· 采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题。
· 采用I/O多路复用模型,非阻塞IO
用户空间和内核空间

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

Redis网络模型
就是使用 I/O 多路复用结合事件的处理器来应对多个 Socket 请求
- 连接应答处理器
- 命令回复处理器:在 Redis 6.0 之后,为了提升更好的性能,使用了多线程来处理回复事件
- 命令请求处理器:在 Redis 6.0 之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)