Redis面试题

为什么要用缓存?

  1. 存储session 因为系统是分布式的,通过Nginx转发可能会转发到不同的机器上,http又是无状态的,所以需要用Redis存储session,当Session不在内存中时就会去CacheDB中查找(要求Redis支持持久化),找到则复制到本机,实现Session共享和高可用。
  2. 实现高性能 不走数据库,走缓存,时间短,效率高
  3. 实现高并发 如果系统高峰期一秒钟过来的请求有1万,那一个mysql单机绝对会死掉。你这个时候就只能上缓存,把很多数据放缓存,别放mysql。缓存功能简单,说白了就是key-value式操作,单机支撑的并发量轻松一秒几万十几万,支撑高并发so easy。单机承载并发量是mysql单机的几十倍。

缓存会有什么问题?

  1. 数据库与缓存双写一致性
  2. 缓存穿透
  3. 缓存雪崩

缓存雪崩的发生以及解决方案?

缓存雪崩:因为缓存服务器宕机,导致所有请求直接打到数据库,数据库扛不住高并发的请求,就会挂掉导致整个系统死了。
缓存雪崩解决方案:
事前:保证redis集群必须高可用,主从+哨兵 或者 cluster

事中:本地使用ehcache做个小缓存+ hystrix限流&降级,避免MySQL被打死
Redis面试题_第1张图片

事后:redis持久化,快速恢复缓存数据

缓存穿透现象以及解决方案

缓存与数据库中都没有这种数据,不断的攻击数据库,导致数据库宕机
解决方案:将没有数据的key写到缓存中,val设为一个固定的值,之后再发这种请求,就会直接从缓存中查,避免了缓存穿透


Redis面试题_第2张图片

Redis单线程模型

文件事件处理器+队列+文件事件分派器
文件事件处理器是单线程运行的,包裹了socket,io多路复用程序,队列,文件事件分派器,事件处理器。这一个线程会轮询io多路复用程序,通过他去监听所有的socket,反复去轮询,监听,只要有事件产生,就会压到队列里面,排队拿出来判断需要哪个事件处理器处理,处理完之后,再拿出另一个socket。

Redis面试题_第3张图片
01_redis单线程模型.png

单线程的redis为什么这么快?

主要有三点:

  1. 纯内存操作,redis完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
  2. 采用单线程,避免了不必要的上下文切换;
  3. 采用了非阻塞IO多路复用机制
    io多路复用程序只负责监听+压队列,监听+压队列,不负责处理这些连接。所以是非阻塞的。不会耽误时间,处理事件是靠的事件处理器,事件处理器高效是因为纯内存操作。
     
      这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。

Redis都有哪些数据类型,分别在哪些场景下应用?

String: 做简单的KV缓存

List:有顺序的列表,
微博,某个大v的粉丝,就可以以list的格式放在redis里去缓存
key=某大v
value=[zhangsan, lisi, wangwu]
比如可以通过list存储一些列表型的数据结构,类似粉丝列表了、文章的评论列表了之类的东西

Hash: 主要可以存放对象,把一些简单的对象给缓存起来,后续可以仅仅修改这个对象中某个字段的值。
key=150
value={
“id”: 150,
“name”: “zhangsan”,
“age”: 20
}

Set:
无序集合,自动去重

直接基于set将系统里需要去重的数据扔进去,自动就给去重了,如果你需要对一些数据进行快速的全局去重,你当然也可以基于jvm内存里的HashSet进行去重,但是如果你的某个系统部署在多台机器上呢?

得基于redis进行全局的set去重

可以基于set玩儿交集、并集、差集的操作,比如交集吧,可以把两个人的粉丝列表整一个交集,看看俩人的共同好友是谁?对吧

把两个大v的粉丝都放在两个set中,对两个set做交集

Sorted Set:
排序的set,去重但是可以排序,写进去的时候给一个分数,自动根据分数排序,这个可以玩儿很多的花样,最大的特点是有个分数可以自定义排序规则

比如说你要是想根据时间对数据排序,那么可以写入进去的时候用某个时间作为分数,人家自动给你按照时间排序了

排行榜:将每个用户以及其对应的什么分数写入进去,zadd board score username,接着zrevrange board 0 99,就可以获取排名前100的用户;zrank board username,可以看到用户在排行榜里的排名


解释一下redis的过期策略以及内存淘汰机制?手写LRU算法

redis采用的过期策略是定期删除+惰性删除策略
为什么不用定期删除
定时删除,用一个定时器监视key,过期则自动删除,虽然内存及时释放,但非常消耗CPU资源,在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略.
定期删除+惰性删除是如何工作的呢?
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。
于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
采用定期删除+惰性删除有没有什么问题
有问题,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。
在redis.conf中有一行配置
# maxmemory-policy volatile-lru
该配置就是配内存淘汰策略的(什么,你没配过?好好反省一下自己)
1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。
2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用,目前项目在用这种。
3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。应该也没人用吧,你不删最少使用Key,去随机删。
4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把redis既当缓存,又做持久化存储的时候才用。不推荐
5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。依然不推荐
6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。不推荐
ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。


redis和数据库双写一致性问题?

数据库和缓存双写,就必然会存在不一致的问题。答这个问题,先明白一个前提。就是如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。另外,我们所做的方案其实从根本上来说,只能说降低不一致发生的概率,无法完全避免。因此,有强一致性要求的数据,不能放缓存。


你可能感兴趣的:(Redis面试题)