欢迎关注WX公众号:“程序猿补课班”,分享Java相关技术知识,学习经验,面试经验等。小伙伴快来补课吧!
正文开始 面试官:看你简历上用到了Redis,你们为啥用Redis?
面试者mini王:
为啥用 为啥用,经理让用就用呗。
NO NO,,, 系统中会存在一些缓存数据,而且缓解数据库的压力,所以用到了redis中间件。
面试官:那Redis 支持哪几种数据类型?
面试者mini王:
这so easy
String、List、Set、Sorted Set、hash
面试官:单线程的redis为什么这么快呢?
面试者mini王:
(一)纯内存操作
(二)单线程操作,避免了频繁的上下文切换
(三)采用了非阻塞I/O多路复用机制
面试官:什么是I/O多路复用呢?
面试者mini王:
嗯 果然会问。。还好有准备 嘿嘿
I/O 多路复用模型是利用select、poll、epoll可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll是只轮询那些真正发出了事件的流),依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),且Redis在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈),主要以上两点造就了Redis具有很高的吞吐量。
面试官:redis持久化的几种方式有哪些?
面试者mini王:
Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制.
RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。
AOF持久化,则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。
面试官:来说说缓存击穿、缓存穿透、缓存雪崩有什么区别吧
面试者mini王:
缓存击穿是指缓存中没有但数据库中有的数据,这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
为了防止接下来问怎么解决呢 我就直接回答解决方案了 聪明的我。。。
解决方案是:
设置热点数据永远不过期。或者加互斥锁
缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案:
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。
缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案:
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。
给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。
面试官:过期键的删除策略有哪些呢?
面试者mini王:
其实Redis采用的是定期删除+惰性删除策略。
还会附带设置内存淘汰策略
noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key。(推荐使用,目前项目在用这种)(最近最久使用算法)
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 Key。(应该也没人用吧,你不删最少使用 Key,去随机删)
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 Key。这种情况一般是把 Redis 既当缓存,又做持久化存储的时候才用。(不推荐)
volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 Key。(依然不推荐)
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。(不推荐)
面试官:Redis 和数据库双写一致性问题怎么处理呢?
面试者mini王:可以保证最终一致性
采用延时双删策略。
(1)先淘汰缓存
(2)再写数据库
(3)休眠1秒,再次淘汰缓存
面试官:那如果删缓存失败了怎么办?
面试者mini王:
提供一个补偿措施即可,例如利用消息队列
面试官:你使用过Redis分布式锁么?
面试者mini王:先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。(注意这里是可以同时把setnx和expire合成一条指令来用的!)
还有zookeeper的分布式锁的大致思想为:每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。完成业务流程后,删除对应的子节点释放锁。
面试官:Redis如何实现延时队列
面试者mini王:最简单的方式,定时扫表,或者使用Redis的zset、list的特性,我们可以利用redis来实现一个延迟队列RedisDelayQueue
使用RabbitMq或者其他MQ改造实现延迟队列。
如有错漏之处,敬请指正