Redis常见面试题

一.Redis 支持的数据类型有哪些?

基本数据类型

  • String:存放的是k-v键值对。如:set k v;
    • 使用场景:常规计数,缓存等
  • List:有序,可重复。如:lpush mylist v1 v2 v3;
    • 使用场景:Redis的list是每个子元素都是String类型的双向链表, 可以通过push和pop操作从列表的头部或者尾部 添加或者删除元素,这样List即可以作为栈和队列
  • Set:无序,不可重复。如:sadd myset v1 v2 v3;
    • 使用场景:交集:一些共同关注,共同喜好,二度好友(共同好友)都可以使用。
  • Hash:在 Redis中哈希类型是指值本身是一种键值对结构,如 value={{field1,value1},……{fieldN,valueN}}。如:hmset myhash k1 v1 k2 v2 k3 v3
    • 使用场景:存储对象,存储一些复杂的k-v信息
  • zSet:有序,不可重复。虽然set是无序的,但是zSet就是为了让set有序。如:zadd myset 1 v1 2 v2 3 v3 
    • 使用场景:排行榜。   

三大特殊的数据类型

  • Geospatial:这个功能可以将用户给定的地理位置信息(经纬度)储存起来, 并对这些信息进行操作(计算两地的距离,范围等)。
    • 使用场景:地图,附件的人...
  • HyperLogLog:可以存储不重复的数据,并且占用很小的内存。虽然set也可以,但是占用空间大。如:pfadd myhyper 1 3 5 6 7 4 
    • 每个 HyperLogLog 只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。
    • 使用场景:适合浏览量一类的业务。
  • BitMap位图:存放是/否的操作,如果把true/false放在数据库中,占用空间大。BitMap采用二进制01110010101001...进行存储。如:setbit mybit 7 1;//比如第7天是否打卡
    • 使用场景:存放登录/未登录,打卡/未打卡,活跃/不活跃这样的数据
二.RDB和AOF机制

它们都是redis进行持久化的方式。

  • RDB:(redis database)
    • 在指定的时间间隔内将内存中的数据集快照写入磁盘(dump.rdb)。 恢复时将快照文件直接读到内存里,它会fork一个子进程来进行持久化,不会影响主进程的性能。
    • 优点:效率高,因为fork一个子进程进行IO操作,不影响主进程。
    • 缺点:安全性不高 
      • 在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改
      • Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑。
    • 同步机制:redis会自动进行持久化
      •  save 60 10  //在redis.config中手动设置:60秒内修改10次
      • 执行flushall 命令的时候
      • 退出redis.server的时候
  • AOF (append only file)
    • 日志的形式来记录每一个"写/删除"的命令。但是只追加文件不可以修改文件,redis启动的时候会重新加载appendonly.aof,重新执行所有的命令,用来恢复数据
    • 优点:安全性高
      • 数据安全性高,发生宕机后,不会破坏已经存在的内容
      • 内部采用rewrite模式,定期对AOF文件进行重写,以达到压缩的目的
    • 缺点:效率不高
      • AOF文件比RDB文件要大很多,并且每次启动重新执行写的指令,效率就低。
    • 同步机制: 不同步;每秒同步一次;每次写入时同步
  • 并存:
    • redis支持同时开启两种方式,默认优先载入aof文件来恢复数据,因为数据完整性高。 rdb作为备份。  
三.过期键删除策略

redis的过期策略是指:key过期了,redis如何在内部把它删除。

  • 惰性过期:只有在使用key的时候,才判断key是否过期,过期清除。不使用的时候就算是过期了,也一直保存在内存中。
    • 可以节省CPU资源,但对内存不友好。
  • 定时过期:不是redis的策略,指给每一个key都配上定时器。到时间了自动清除key。
    • 可以节省内存,但是对CPU不友好,因为得维护每一个定时器。
  • 定期过期:每隔一段时间,会扫描一定数量的expires字典中的key,删除过期的key。
    • 是对以上两者的一个折中方案,平衡内存和CPU的最高性能。

redis默认同时使用了惰性过期和定期过期两种策略

四.线程模型

线程模型:

  • 多个 socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 IO多路复用程序会监听多个 socket,会将产生事件的 socket 放入队列中排队,事件分派器每次从队列中取出一个 socket,根据 socket 的事件类型交给对应的事件处理器进行处理。

Redis常见面试题_第1张图片

 一次客户端与redis完整通信过程

  • 建立连接
    1. 首先,redis 服务端进程初始化的时候,会将 server socket 的 AE_READABLE 事件与连接应答处理器关联。
    2. 客户端 socket01 向 redis 进程的 server socket 请求建立连接,此时 server socket 会产生一个 AE_READABLE 事件,IO 多路复用程序监听到 server socket 产生的事件后,将该 socket 压入队列中。
    3. 文件事件分派器从队列中获取 socket,交给连接应答处理器。
    4. 连接应答处理器会创建一个能与客户端通信的 socket01,并将该 socket01 的 AE_READABLE 事件与命令请求处理器关联。
  •  执行一个set请求
    1. 客户端发送了一个 set key value 请求,此时 redis 中的 socket01 会产生 AE_READABLE 事件,IO 多路复用程序将 socket01 压入队列,
    2. 此时事件分派器从队列中获取到 socket01 产生的 AE_READABLE 事件,由于前面 socket01 的 AE_READABLE 事件已经与命令请求处理器关联,
    3. 因此事件分派器将事件交给命令请求处理器来处理。命令请求处理器读取 socket01 的 key value 并在自己内存中完成 key value 的设置。
    4. 操作完成后,它会将 socket01 的 AE_WRITABLE 事件与命令回复处理器关联。
    5. 如果此时客户端准备好接收返回结果了,那么 redis 中的 socket01 会产生一个 AE_WRITABLE 事件,同样压入队列中,
    6. 事件分派器找到相关联的命令回复处理器,由命令回复处理器对 socket01 输入本次操作的一个结果,比如 ok,之后解除 socket01 的 AE_WRITABLE 事件与命令回复处理器的关联。

 Redis常见面试题_第2张图片

redis单线程快的原因?

  • 纯内存操作。
  • 核心是基于非阻塞的 IO 多路复用机制。
  • C 语言实现,语言更接近操作系统,执行速度相对会更快。
  • 单线程反而避免了多线程的频繁上下文切换问题,预防了多线程可能产生的竞争问题。
  • 总结:就像对于一个请求来说,如果业务逻辑复杂,使用多线程效率就高,用户也可以及时得到回应.。但是请求中如果业务逻辑简单(就像redis,就仅仅是对key进行读写操作),但是请求量大的话, 单线程效率就要高。
 五.缓存雪崩,缓存击穿,缓存穿透的区别?
  • 缓存雪崩:指缓存(redis)同一时间,大面积的失效。所有请求都到了数据库,相当于redis整个不能用了。
    • 比如:redis重启的时候,开始redis中没有任何数据;或者缓存数据同一时间失效; 这时所有请求需要访问数据库,那一刻相当于缓存雪崩,整个redis不能用了。 同时访问大量数据
    • 解决方案:
      • 设置随机过期时间,避免统一时间点同时过期
      • 缓存预热:比如对于重启的时候,提前把一些数据放到缓存中, 让redis一启动就加载了热点数据
      • 互斥锁:加锁,访问数据库的请求只能有一条。
  • 缓存击穿:是指访问缓存中没有,但是数据库中有的数据。与缓存雪崩不同的是,它是同时访问同一条热点数据。而不是大量的数据。同时访问同一数据
    • 解决方案:
      • 设置热点数据永不过期,但是也需要维护(定期删除)
      • 互斥锁
  • 缓存穿透:同时大量访问缓存中和数据库中都不存在的数据,一般就是被恶意攻击。
    • 解决方案:
      • 接口层面添加校验,比如查询的id不符合数据库中的范围,或者用户权限不够。直接拦截
      • 缓存空对象,把该数据的key存在缓存中,值设为null。这样可以防止反复用同一个id暴力攻击
      • 使用布隆过滤器,这是redis自带的。它将所有可能的数据存放在足够大的bitmap中,一个一定不存在的数据就会被这个bitmap拦截掉,避免数据库的查询压力      
六.redis事务

既然是事务,就需要保证ACID。

  • 原子性(A):事务中单条命令才保证原子性,整个事务并不保证原子性。因为事务不支持回滚
  • 一致性(C):Redis舍弃了回滚的设计,基本上也就舍弃对数据一致性的有效保证。
  • 隔离性(I):redis单线程天然的隔离性
  • 持久性(D):采用RDB和AOF两种方式实现持久化

实现:Redis事务的三个阶段

  • 开始事务 (multi)
  • 命令入队 (命令),采用FIFO (先进先出)
  • 执行事务 (exec)

注意:事务中如果出现命令语法错误(相当于编译时错误),所有命令就不会执行。 事务中如果出现逻辑错误(相当于运行时错误),错误语句不执行,其他命令正常执行。

事务中用到的其他命令:

  • watch:相当于加了一个乐观锁,更新时判断。
  • unwatch:释放乐观锁。但是每次事务结束(无论成功与否)会自动释放乐观锁,不需要手动执行该命令
  • discard:事务中途放弃事务,命令都不执行   
七.redis集群

redis的集群方案?  四种模式各有优缺点,可根据实际场景进行选择。

  • 主从模式
    • 指将一台Redis服务器的数据,复制到其他的Redis服务器。 数据的复制是单向的,只能由主节点到从节点。
    • Master以写为主,可读;Slave以读为主,不能写。 实现读写分离。
    • 缺点:不能自动故障转移
  • 哨兵模式
    • 基于主从复制模式,哨兵本身就是redis一个实例,用来监视其他的实例。主节点宕机,采用选老大的方式处理故障,实现自动故障转移。
    • 缺点:不能在线扩容
  • redis Sharding模式(客户端分片)
    • 客户端通过哈希算法将数据的key进行散列,映射到不同的redis节点中。Jedies就支持 Sharding功能。解决了在线扩容问题
    • 缺点:大量的处理放在客户端,导致数据大。
  • redis cluster模式(服务端分片)
    • 是redis Sharding模式的升级版,将集群分为不同的卡槽(0~16383),每个节点有一定数量的卡槽。 每一节点既是主节点,也是从节点,相互依赖。解决了扩容问题
    • 缺点:对于像批量操作,事务操作等的支持性不够好

 

哨兵模式选老大的算法?

  • 哨兵通过发送命令,等待Redis服务器响应。如果主机宕机,哨兵1先检查到这个信息, 但是不会马上进行选举。这个过程称为主观下线
  • 当其他的哨兵也检查到主机宕机,那么哨兵之间会进行投票选取新的老大,选取成功之后,会通过发布订阅模式,让各个从节点切换主机,这个过程成为客观下线
  • 注意:如果已经客观下线,就算主节点已经恢复,只能作为新的主节点的从节点

 

Cluster如何实现故障转移

  • Redis 集群节点采用 Gossip 协议来广播自己的状态以及自己对整个集群认知的改变。比如一个节点发现某个节点失联了 (PFail),它会将这条信息向整个集群广播,其它节点也就可以收到这点失联信息。
  • 如果一个节点收到了某个节点失联的数量 已经达到了集群的大多数,就可以标记该节点为确定下线状态 (Fail),然后向整个集群广播,强迫其它节点也接收该节点已经下线的事实,并立即对该失联节点进行主从切换。

 

主从复制的原理?

  • 全量复制
    • 主节点进行RDB或者AOF,然后将持久化文件通过网络发送给从节点,从节点开始启动的时候进行读取,此时从节点是同步的,不能进行数据访问(因为也没有数据),需要消耗IO资源。
  • 增量复制
    • 每次主节点更新数据,会发送给从节点,从节点根据偏移量进行增量复制。
    • 注意:因为IO非常消耗资源,每次增量进行持久化的时候,会把更新的数据放在主节点的一个叫做 复制积压缓存区的地方,发送给从节点。这样就不至于每次增量复制进行io操作。

Redis常见面试题_第3张图片

上图相关问题:

  • runid:每一个redis实例都有一个id,区别身份。比如主节点发送给从节点就会把自己的id发送过来。主节点宕机了,这个不能使用增量复制。    
  • 什么时候会由增量复制变为全量复制?  
    • runid不匹配(比如主节点宕机了,不是之前那个了),或者复制积压缓存区放不下。 

 

 

寄语:你所浪费的今天是昨天死去的人奢望的明天;你所厌恶的现在,是未来的你回不到的曾经   ---哈佛大学校训

 线程模型参照文章:https://www.cnblogs.com/mrmirror/p/13587311.html

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