redis相关面试题

5 篇文章 0 订阅
3 篇文章 0 订阅

1.Redis有哪些数据结构?

字符串String、字典Hash、列表List、集合Set、有序集合SortedSet。

Redis中高级用户,还需要加上下面几种数据结构HyperLogLog、Geo、Pub/Sub。

(   1.1 HyperLogLog 是用来做基数统计的算法,有误差在0.89%左右,计算基数所需的空间总是固定的、并且是很小的。不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。,而是返回集合去重后的大小,做统计使用,比如访问量巨大的uv统计;HyperLogLog 提供了两个指令 pfadd 和 pfcount

     1.2 Geo:

         Redis在3.2版本以后增加了地理位置的处理,其提供了6个地理位置相关的命令

              1.2.1.GEOADD 将给定的空间元素(纬度、经度、名字)添加到指定的键里面

              1.2.2.GEOPOS 从键里面返回所有给定位置元素的位置(经度和纬度)

              1.2.3.GEODIST 返回两个给定位置之间的距离。

              1.2.4.GEORADIUS 以给定的经纬度为中心, 返回与中心的距离不超过给定最大距离的所有位置元素。

              1.2.5.GEORADIUSBYMEMBER 跟GEORADIUS类似

              1.2.6.GEOHASH 返回一个或多个位置元素的 Geohash 表示。

1.3.Pub/Sub

发布者和订阅者。订阅者可以订阅一个或多个频道,而发布者可以向指定的 频道发送消息,所有订阅此频道的订阅者都会收到该消息;发送消息返回值表示接收到这条消息的订阅者数量。在执行这条命令的时候还没有订阅者订阅该频道,返回0. 另外值得注意的是消息发送出去不会持久化,如果发送之前没有订阅者,那么后续再有订阅者订阅该频道,之前的消息就收不到了);

 

2.使用过Redis分布式锁么,它是什么回事?

先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。

在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?

锁就永远得不到释放了。set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!

(set key value [EX seconds] [PX milliseconds] [NX|XX]

EX seconds:设置失效时长,单位秒

PX milliseconds:设置失效时长,单位毫秒

NX:key不存在时设置value,成功返回OK,失败返回(nil)

XX:key存在时设置value,成功返回OK,失败返回(nil))

3.假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?

使用keys指令可以扫出指定模式的key列表。

如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?

redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。

可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。

4.使用过Redis做异步队列么,你是怎么用的?

一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。

如果对方追问可不可以不用sleep呢?list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。

能不能生产一次消费多次呢?使用pub/sub主题订阅者模式,可以实现1:N的消息队列。

pub/sub有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。

redis如何实现延时队列?

使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。

5.缓存雪崩

缓存雪崩是指设置缓存时采用了相同的过期时间,导致缓存在某一个时刻同时失效,或者缓存服务器宕机宕机导致缓存全面失效,请求全部转发到了DB层面,DB由于瞬间压力增大而导致崩溃。缓存失效导致的雪崩效应对底层系统的冲击是很大的。

解决方式

\1. 对缓存的访问,如果发现从缓存中取不到值,那么通过加锁或者队列的方式保证缓存的单进程操作,从而避免失效时并发请求全部落到底层的存储系统上;但是这种方式会带来性能上的损耗

\2. 将缓存失效的时间分散,降低每一个缓存过期时间的重复率

\3. 如果是因为缓存服务器故障导致的问题,一方面需要保证缓存服务器的高可用、另一方面,应用程序中可以采用多级缓存

 

6.缓存穿透

缓存穿透是指查询一个根本不存在的数据,缓存和数据源都不会命中。出于容错的考虑,如果从数据层查不到数据 ,则不写入缓存,即数据源返回值为 null 时,不缓存 null。缓存穿透问题可能会使后端数据源负载加大,由于很多后端数据源不具备高并发性,甚至可能造成后端数据源宕掉

解决方式

\1. 如果查询数据库也为空,直接设置一个默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。比如,”key” , “&&”。

在返回这个&&值的时候,我们的应用就可以认为这是不存在的key,那我们的应用就可以决定是否继续等待继续访问,还是放弃掉这次操作。如果继续等待访问,过一个时间轮询点后,再次请求这个key,如果取到的值不再是&&,则可以认为这时候key有值了,从而避免了透传到数据库,从而把大量的类似请求挡在了缓存之中。

\2. 根据缓存数据Key的设计规则,将不符合规则的key进行过滤

 

7.更新数据库的同时为什么不马上更新缓存,而是删除缓存?
 因为考虑到更新数据库后更新缓存可能会因为多线程下导致写入脏数据(比如线程 A 先更新数据库成功,接下来要取更新缓存,接着线程 B 更新数据库,但 B 又更新了缓存,接着 B 的时间片用完了,线程 A 更新了缓存)

 

8.Redis如何做持久化的?

bgsave做镜像全量持久化,aof做增量持久化。

因为bgsave会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要aof来配合使用。

在redis实例重启时,会使用bgsave持久化文件重新构建内存,再使用aof重放近期的操作指令来实现完整恢复重启之前的状态。

对方追问那如果突然机器掉电会怎样?取决于aof日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。

但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1s1次,这个时候最多就会丢失1s的数据。

对方追问bgsave的原理是什么?你给出两个词汇就可以了,fork和cow。

fork是指redis通过创建子进程来进行bgsave操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。

 

9.Redis内存回收策略

Redis中提供了多种内存回收策略当内存容量不足时,为了保证程序的运行,这时就不得不淘汰内存中的一些对

象,释放这些对象占用的空间,那么选择淘汰哪些对象呢?

其中,默认的策略为noeviction策略,当内存使用达到阈值的时候,所有引起申请内存的命令会报错

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

适合的场景: 如果我们的应用对缓存的访问都是相对热点数据,那么可以选择这个策略

allkeys-random:随机移除某个key。

适合的场景:如果我们的应用对于缓存key的访问概率相等,则可以使用这个策略

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰。

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

适合场景:这种策略使得我们可以向Redis提示哪些key更适合被淘汰,我们可以自己控制

 

10.Redis是单进程单线程?性能为什么这么快

     Redis采用了一种非常简单的做法,单线程来处理来自所有客户端的并发请求,Redis把任务封闭在一个线程中从而避免了线程安全问题;redis为什么是单线程?

官方的解释是,CPU并不是Redis的瓶颈所在,Redis的瓶颈主要在机器的内存和网络的带宽。那么Redis能不能处理高并发请求呢?当然是可以的,至于怎么实现的,我们来具体了解一下。 【注意并发不等于并行,并发性I/O流,意味着能够让一个计算单元来处理来自多个客户端的流请求。并行性,意味着服务器能够同时执行几个事情,具有多个计算单元

多路复用

      Redis 是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞

的,所以 I/O 操作在一般情况下往往不能直接返回,这会导致某一文件的 I/O 阻塞导致整个进程无法对其它客户提

供服务,而 I/O 多路复用就是为了解决这个问题而出现的。

了解多路复用之前,先简单了解下几种I/O模型

(1)同步阻塞IO(Blocking IO):即传统的IO模型。

(2)同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。

(3)IO多路复用(IO Multiplexing):即经典的Reactor设计模式,也称为异步阻塞IO,Java中的Selector和 Linux中的epoll都是这种模型。

(4)异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。

同步和异步、阻塞和非阻塞,到底是什么意思,感觉原理都差不多,我来简单解释一下

同步和异步,指的是用户线程和内核的交互方式,即是否等待io完成

阻塞和非阻塞,指用户线程调用内核IO操作的方式是阻塞还是非阻塞

就像在Java中使用多线程做异步处理的概念,通过多线程去执行一个流程,主线程可以不用等待。而阻塞和非阻塞

我们可以理解为假如在同步流程或者异步流程中做IO操作,如果缓冲区数据还没准备好,IO的这个过程会阻塞

 

11.Pipeline有什么好处,为什么要用pipeline?

可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。

使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。

 

12.Redis的同步机制了解么?

Redis可以使用主从同步,从从同步。

第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。

加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

 

13.是否使用过Redis集群,集群的原理是什么?

Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。

Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。

 

14.为啥 redis 单线程模型也能效率这么高?

  • 纯内存操作
  • 核心是基于非阻塞的 IO 多路复用机制
  • 单线程反而避免了多线程的频繁上下文切换问题

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值