前言
本篇是整理自己遇到或看到关于redis相关的面试题,答案仅供参考。
`提示:写博客是自己对知识梳理,目前是写给自己看,算是自己学习后的作业,也是为了养成一个良好的习惯。
一、基础篇
redis 为什么那么快?
1. Redis是单线程模型,没有上下文切换的开销;
2. 简单的数据类型,时间复杂度都是O(1);
3. 数据都在内存中,计算快;
4. 采用了多路复用机制使其在网络IO操作中能并发处理大量的客户端请求,实现高吞吐率。
为什么说redis是单线程?
Redis是单线程主要是指 Redis的网络IO和键值对读写是由一个线程来完成的,
这也是Redis对外提供键值存储服务的主要流程。
既然redis是单线程为什么redis 6.0要改成多线程?
这个官网有原话,大概就是因为redis所有的操作都在内存中,则CPU不是redis瓶颈,随着
科技发展内存也不再那么昂贵,那么redis主要性能问题就是在单线程的IO同步阻塞问题,则在6.0时
对于持久化、异步删除、集群数据同步等同步阻塞功能支持了多线程处理,解决同步阻塞问题。
什么是IO多路复用?redis是那种reactor的网络模型?
IO多路复用就是通过一种机制,一个线程可以监听多个描述符(socket),
一旦某个描述符就绪,就通知程序(handler)进行相应的读写操作。
redis是单reactor多线程的网络模型,具体请看下图:
二、缓存问题篇
1.经典缓存问题
缓存穿透问题
场景:缓存中没有数据,DB中也没有数据,请求直接打在DB上。
方案:布隆过滤器;加强接口的校验,过滤掉无效的请求;
闪电缓存,设置短时间的过期key,key为请求唯一标识,值为null。
缓存击穿问题
场景:一般是热key刚好过期时,同时有大量请求直接打在了DB上。
方案:热key数据不设置过期时间;在数据层做互斥锁。
缓存雪崩问题
场景:缓存大面积失效,导致大量的请求直接打在DB上;
方案:缓存集中过期:将过期时间用随机数打散;服务宕机:采用高可用集群模式;服务做熔断降级保护
缓存污染问题
场景:热key和其他key没有分开,在高并发时会存在新的缓存将热key缓存淘汰掉,从而导致的缓存污染
方案:热key分开存放,保证缓存不被污染;淘汰策略由LRU关注于访问时间改为LFU关注于访问频率。
2.缓存一致性问题
缓存和DB一致性问题
场景:保证缓存和DB数据一致性问题
方案:最终一致性--延迟双删,保证最多在延迟时间内数据不一致,具体的看下图的分析;
强一致性--读写串行,这种虽然保证了强一致性但是违背了使用缓存的初衷,不推荐。
但是在实际开发中用得最多的还是懒加载模式。
三、性能优化篇
关于性能优化的一些问题我的上一篇已经有详细从 网络IO、内存利用率、单线程的同步阻塞等
多个维度思考、分析并给出了相关的方案,具体请参考我之前写 redis优化的思考。
相关问题有以下:
1. redis性能瓶颈在那?如何优化?
2. bigkey问题,如何探测bigkey,有哪些方案?
3. redis如何节约内存?
可以看一下以后这张图加强你对redis相关问题的思维导向。
四、集群篇
谈谈常见redis集群有哪些?各自解决问题?
主从模式,关注于数据备份容灾问题,保证数据的高可靠,异常时需要人工接入;
哨兵模式,关注的是服务的高可用,即异常时自动切换主从;
切片模式(cluster),支持高可用,数据备份,关注的是高性能,数据分片存储,支持横向扩展。
1.哨兵集群
说一说哨兵集群和cluster集群的组成和作用?
组成:n*zk(奇数)+ master redis+ n*slave redis
作用:多个zk(奇数 zk的一致性算法ZAB)组成的哨兵是支持对redis集群的监控和选举,
可以实现自动对故障转移,1主多从是为了实现数据备份和异常时主从切换。
ps:这里就不详细说明了,会抽时间专门的写一篇关于redis集群的博客。
谈一谈哨兵集群中,哨兵的作用?
1. 监控:Sentinel不断的检查主服务和从服务器是否按照预期正常工作;
2. 提醒:被监控的Redis出现问题时,Sentinel会通知管理员或其他应用程序;
3. 自动故障转移:监控的主Redis不能正常工作,Sentinel会开始进行故障迁移操作。
哨兵集群中,突然主节点宕机了会发生什么?
1. 删除列表中所有处于下线或者短线状态的Slave;
2. 删除列表中所有最近5s内没有回复过领头Sentinel的INFO命令的Slave;
3. 删除所有与下线Master连接断开超过down-after-milliseconds * 10毫秒的Slave;
4. 领头Sentinel将根据Slave优先级,对列表中剩余的Slave进行排序,并选出其中优先级最高的Slave。
如果有多个具有相同优先级的Slave,那么领头Sentinel将按照Slave复制偏移量,选出其中偏移量最大的Slave。
如果有多个优先级最高,偏移量最大的Slave,那么根据运行ID最小原则选出新的Master。
2.Cluster集群
说一说cluster集群的组成和作用?
组成:Master/Slave +Master/Slave+Master/Slave
目的:数据分片存储,解决是大数据内存瓶颈的问题
ps:这里就不详细说明了,会抽时间专门的写一篇关于redis集群的博客。
既然Cluster集群是支持横向扩展,那么它能否无限扩展?如果不能最大是多少?为什么?
最多支持16384个节点,crc16 算法将数据分布到不同节点上,具体的算法是
crc16(key)mod 16384,则计算出的key都会对应一个编号在 0-16383 之间的哈希槽,
即Cluster集群最大支持16384个节点
redis Cluster集群中,突然宕机了一台会发生什么?
1. 如果半数以上 Master 处于关闭状态那么整个集群处于不可用状态。
2. 关闭任意一对主从节点会导致部分(大约为整个集群的1/3)失败。
3. 关闭任意一主,会导致部分写操作失败,是由于从节点不能执行写操作,
在Slave升级为Master期间会有少量的失败。
4. 关闭从节点对于整个集群没有影响。
3.主从复制
redis主从复制过程?
1、slave向master发起同步请求,slave会告知master自己的offset,如果是第一次,offset=-1表示需要全量同步;
2、slave连上了master,master会给slave分配一个buffer(复制缓冲区);
3、master fork子进程dump RDB文件,dump完成后告诉父进程,父进程把RDB发给slave(通过slave的socket发过去);
4、slave载入rdb到本地磁盘;
5、在master dump RDB期间,master所有写命令,都写到这个buffer然后同步给slave
6、slave执行同步过来的buffer命令。
redis是如何解决一主多从同步会有什么问题?怎么解决?
从主从复制过程可以看出,每有一个slave来请求同步数据master都会分配复制缓冲区,通过socket
将数据都同步给slave,则slave越多master同步的开销越大。
方案:1. 控制从节点的数量;2.启用级联模式进行主从同步。
什么是脑裂?脑裂问题如何处理?
在哨兵集群中,master节点由于网络问题同zk集群断掉了,则zk集群(哨兵)认为master宕机了会在
slave节点中选举一个新master,则当前哨兵集群中有2个master,而之前客户端依旧会往原master写
数据,当原master同zk集群网络恢复后会先删掉自己的数据然后去全量同步新master数据,这就会导致数据
丢失,以上整个过程就是脑裂问题。
先来看一下关于脑裂网上常见的处理方案:
min-slaves-to-write:1 表示至少要有一个slave的master才能处理数据
min-slaves-max-lag:10 表示数据的复制和同步时间不能超过10s
1. min-slaves-to-write:1 是可以保证当master和zk集群网络异常时,即便出现脑裂了,
原master也不会处理任何请求,保证数据一致性。
2. min-slaves-max-lag:10 可以保证只要master宕机了,最多只丢10s数据。
以上方案看起来可以,但是都是基于保证一致性牺牲可有用性的维度来思考的,但是我个人认为在实际开
发中应该通过业务要求来取舍一致性和可用性,如果业务一致性要求很高的话上面方案是ok,但是redis是
更多作为缓存中间件在实际开发中我们使用(有DB兜底),它更多的需要保证是可用性。
基于上面分析我更倾向于尽可能的减少出现脑裂情况,即从运维出发将哨兵集群和redis集群部署在同一个局域网中,
尽可能减少哨兵和redis的网络问题。
主从复制过程图:
主从级联模式图:
五、持久化篇
redis支持哪些持久化?
RDB快照、AOF日志、混合持久化(RDB+增量AOF)redis4.0支持。
RDB原理和过程?
说简单点就是把内存数据持久化到磁盘,使用的是操作系统的多进程COW(写时即复制)机制来实现快照持久化,
具体过程就是 父进程调用fork子进程去遍历内存数据,将内存数据二进制序列化到磁盘上,子进程完成后通知父进程。
RDB和AOF区别?
RDB:
1.备份方式:RDB是一次全量备份;
2.数据存储:RDB是内存数据二进制序列化形式,在存储上非常紧凑,即体积较小;
3.数据恢复:由于体积小,则恢复数据非常快;
4.启动:redis默认方式。
AOF:
1. 备份方式:AOF连续的增量备份;
2. 数据存储:AOF日志存储的是redis服务器顺序指令序列,它在长期的运行中会变的无比庞大;
3. 数据恢复:由于AOF体积非常大重放整个AOF非常耗时;
4. 启动:手动设置。
什么是重写AOF?为什么要重写AOF?它的原理?
AOF日志会随着实例一直运行,体积会越来越庞大,为了解决这问题,redis支持了重写AOF功能来给
AOF瘦身。
原理:开辟有个子进程对内存进行遍历,转化成一系列redis的操作指令,序列化到新的AOF日志文件中,
再将操作期间的增量AOF日志追加到新的AOF上,最后用新的AOF替代旧的AOF,从而大大减小了AOF的体积。
redis实例同时开启了AOF和RDB,重启后数据恢复是使用RDB还是AOF?为什么?
会使用AOF,因为AOF的数据更完整,它默认配置可以保证最多丢失1s数据。
混合持久化作用?它的原理?
重启redis时,如果用RDB恢复数据虽然速度快但是会丢失大量的数据,采用AOF重放可以保证数据完整性但是超级慢,为了解决这个问题4.0推出了混合持久化。
原理:AOF在重写时,不再是单纯将内存数据转换为指令命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理,
并且将RDB快照内容和增量的AOF修改内存数据的命令存在一起,都写入新的AOF文件,即混合持久化 =RDB+增量AOF。
fork期间会阻塞父进程吗?为什么会阻塞?如何优化?
fork完成之前,会阻塞父进程,主要是父进程需要拷贝进程中的内存页表给子进程,每个进程都要有自己
的内存页表,所以这个父子进程无法共享,必须要拷贝一份,进程占用的内存越大,拷贝时间越久。
优化:redis4.0可以开启appendfsync everysec配置,将持久化放到后台线程中去执行,
尽量降低 Redis写磁盘对性能的影响。
RDB过程图:
AOF重写过程图:
六、过期策略篇
1. redis支持哪些过期策略?它使用的是哪一个?
定时删除,惰性删除,定期删除三种,它默认使用的是定期删除+惰性删除
2. 谈谈定期删除策略的实现?
为了解决效率问题,定期删除策略并不是遍历整个字典,它采用的是贪心算,具体的如下:
2.1. 从过期字典中挑选出20个key;
2.2. 删除20个key中已过期的key;
2.3. 如果删除的key占比超过了1/4则重复步骤1;
基于以上逻辑为了解决循环过度导致线程卡死的现象,在算法上加上了超时时间的机制,默认时间是25ms。
3. 如果订阅了key失效事件,key过期了收到的通知会有延迟么?为什么?
会延迟,因为redis默认是惰性删除+定期删除,定期删除采用的贪心算法在淘汰key时会存在延迟问题。
4. 在集群中a这个key过期了,但是从slave读到a,它会返回什么给客户端?slave会马上删除a么?为什么?
在redis3.2之前会返回key的值,redis3.2之后会返回给客户端null,salve不会马上删除a,当
a在master节点上被淘汰后,del指令同步给salve时salve才会删除a。
总结
本来只是想写一下常见面试题,但是写着写着之前列举的大纲就变了,这个就不定期的更新补充吧,如果发现以上内容有问题请告知我,谢谢!