Redis面试

学到这里终于对Redis有个初步的了解,今天来看看面试中常常遇到的有关Redis的面试题面试

  • 为何要用Redis
  • Redis 和 Memcached 的区别
  • Redis有哪些数据结构?
  • 什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?
  • 使用过Redis分布式锁么,它是怎么实现的?
  • 什么是缓存穿透?如何避免?
  • 什么是缓存雪崩?如何避免?
  • 如何保证缓存与数据库双写时的数据一致性?

为何要用Redis

  • 加速读写
  • 高并发

为何要用 redis 而不用 map/guava 作缓存?

缓存分为本地缓存分布式缓存。以 Java 为例,使用自带的 map 或者 guava 实现的是本地缓存,最主要的特色是轻量以及快速,生命周期随着 jvm 的销毁而结束,而且在多实例的状况下,每一个实例都须要各自保存一份缓存,缓存不具备一致性。redis

使用Redis或Memcached之类的称为分布式缓存,在多实例的状况下,各实例共用一份缓存数据,缓存具备一致性。缺点是须要保持 redis 或 memcached服务的高可用,整个程序架构上较为复杂。数据库

Redis和Memcached 的区别

  • redis支持更丰富的数据类型(支持更复杂的应用场景)
  • Redis支持数据的持久化,能够将内存中的数据保持在磁盘中,重启的时候能够再次加载进行使用,而Memecache把数据所有存在内存之中。
  • memcached没有原生的集群模式,须要依靠客户端来实现往集群中分片写入数据;可是 redis 目前是原生支持 cluster 模式的.
  • Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的多路 IO 复用模型。

Redis有哪些数据结构

String字符串

格式: set key value
Redis的String能够包含任何数据。好比jpg图片或者序列化的对象 。
String类型是Redis最基本的数据类型,一个键最大能存储512MB。缓存

Hash(哈希)

格式: hmset name  key1 value1 key2 value2
Redis hash 是一个键值对集合。
Redis hash是一个String类型的field和value的映射表,相似于Java中的Map<String,Map<field,value>>,hash特别适合用于存储对象。服务器

List(列表)

Redis 列表是简单的字符串列表,按照插入顺序排序。你能够添加一个元素到列表的头部(左边)或者尾部(右边)
格式: lpush  name  value
在 key 对应 list 的头部添加字符串元素
格式: rpush  name  value
在 key 对应 list 的尾部添加字符串元素网络

Set(集合)

格式: sadd  name  value
Redis的Set是String类型的无序集合。
集合是经过哈希表实现的,因此添加,删除,查找的复杂度都是O(1)。数据结构

Zset(sorted set:有序集合)

格式: zadd  name score value
Redis zset 和 set 同样也是String类型元素的集合,且不容许重复的成员。
不一样的是每一个元素都会关联一个double类型的分数。redis正是经过分数来为集合中的成员进行从小到大的排序
zset的成员是惟一的,但分数(score)却能够重复。多线程

什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?

什么是Redis持久化?

持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。架构

Redis有哪几种持久化方式?

Redis 提供了两种持久化方式:RDB(默认) 和AOF并发

优缺点是什么?

RDB和AOF并不互斥,它俩能够同时使用。

RDB的优势:载入时恢复数据快、文件体积小。
RDB的缺点:会必定程度上丢失数据(由于系统一旦在定时持久化以前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。)

AOF的优势:丢失数据少(默认配置只丢失1秒的数据)。
AOF的缺点:恢复数据相对较慢,文件体积大

若是Redis服务器同时开启了RDB和AOF持久化,服务器会优先使用AOF文件来还原数据(由于AOF更新频率比RDB更新频率要高,还原的数据更完善)

Redis 4.0 对于持久化机制的优化

Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,能够经过配置项aof-use-rdb-preamble开启)。

若是把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样作的好处是能够结合 RDB 和 AOF 的优势, 快速加载同时避免丢失过多的数据。固然缺点也是有的, AOF 里面的 RDB 部分是压缩格式再也不是 AOF 格式,可读性较差。

使用过Redis分布式锁么,它是怎么实现的?

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

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

set指令可使用很是复杂的参数同时把setnx和expire合成一条指令来用的!这样就保证了原子性!

使用过Redis作异步队列么,你是怎么用的?

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

可不能够不用sleep呢?

list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。

能不能生产一次消费屡次呢?

使用pub/sub主题订阅者模式,能够实现1:N的消息队列。

pub/sub有什么缺点?

在消费者下线的状况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。

redis如何实现延时队列?

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

什么是缓存穿透?如何避免?

什么是缓存穿透?

通常的缓存系统,都是按照key去缓存查询,若是不存在对应的value,就到数据库去查找。一些恶意的请求会故意查询不存在的key,致使数据库接收到大量无效请求,就会对服务器形成很大的压力。这就叫作缓存穿透。

如何避免

  • 当咱们从数据库找不到的时候,咱们也将这个空对象设置到缓存里边去。下次再请求的时候,就能够从缓存里边获取了。这种状况咱们通常会将空对象设置一个较短的过时时间
  • 因为请求的参数是不合法的,因而咱们可使用布隆过滤器(BloomFilter)提早拦截,不合法就不让这个请求到数据库层!

什么是缓存雪崩?如何避免?

什么是缓存雪崩?

大量缓存在同一时间集中过时或者Redis挂掉,都会致使缓存同一时间大面积的失效,因此,后面的请求都会落到数据库上,形成数据库短期内承受大量请求而崩掉。

如何避免

对于大量缓存在同一时间集中过时,能够这样解决:

  • 在缓存的时候给过时时间加上一个随机值,这样就会大幅度的减小缓存在同一时间过时。

对于“Redis挂掉了,请求所有走数据库”这种状况,咱们能够有如下的思路:

  • 事发前:实现Redis的高可用(主从架构+Sentinel 或者Redis Cluster),尽可能避免Redis挂掉这种状况发生。
  • 事发中:万一Redis真的挂了,咱们能够设置本地缓存(ehcache)+限流(hystrix),尽可能避免咱们的数据库被干掉(起码能保证咱们的服务仍是能正常工做的)
  • 事发后:redis持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据

如何保证缓存与数据库双写时的数据一致性?

缓存与数据库双写一致性

这里不一致指的是:数据库的数据跟缓存的数据不一致

从理论上说,只要咱们设置了键的过时时间,咱们就能保证缓存和数据库的数据最终是一致的。
除了设置过时时间,咱们还须要作更多的措施来尽可能避免数据库与缓存处于不一致的状况发生。

通常来讲,执行更新操做时,咱们会有两种选择:

  • 先更新数据库,再删除缓存
  • 先删除缓存,再更新数据库

为何是删除缓存而不是更新缓存?

通常咱们都是采起删除缓存缓存策略的,由于删除缓存比更新缓存要简单得多,频繁更新缓存的话会消耗必定性能。

举个例子,一个缓存涉及的表的字段,在 1 分钟内就修改了 20 次,或者是 100 次,那么缓存更新 20 次、100 次;可是这个缓存在 1 分钟内只被读取了 1 次,有大量的冷数据。实际上,若是你只是删除缓存的话,那么在 1 分钟内,这个缓存不过就从新计算一次而已,开销大幅度下降。用到缓存才去算缓存,这也体现了懒加载的思想。

先更新数据库,再删除缓存

若是在高并发的场景下,出现数据库与缓存数据不一致的几率特别低,也不是没有:

  1. 缓存恰好失效
  2. 线程A查询数据库,得一个旧值
  3. 线程B将新值写入数据库
  4. 线程B删除缓存
  5. 线程A将查到的旧值写入缓存

要达成上述状况,仍是说一句几率特别低:

由于这个条件须要发生在读缓存时缓存失效,并且并发着有一个写操做。而实际上数据库的写操做会比读操做慢得多,并且还要锁表,而读操做必需在写操做前进入数据库操做,而又要晚于写操做更新缓存,全部的这些条件都具有的几率基本并不大。

先删除缓存,再更新数据库

咱们在并发场景下分析一下,就知道仍是有问题的了:

  1. 线程A删除了缓存
  2. 线程B查询,发现缓存已不存在
  3. 线程B去数据库查询获得旧值
  4. 线程B将旧值写入缓存
  5. 线程A将新值写入数据库

因此也会致使数据库和缓存不一致的问题。

并发下解决数据库与缓存不一致的思路

将删除缓存、修改数据库、读取缓存等的操做积压到队列里边,实现串行化。

若是你的系统不是严格要求缓存+数据库必须一致性的话,缓存能够稍微的跟数据库偶尔有不一致的状况,最好不要作这个方案,串行化以后,就会致使系统的吞吐量会大幅度的下降,用比正常状况下多几倍的机器去支撑线上的一个请求。

参考

Redis面试题总结
面试前必需要知道的redis面试题

相关文章
相关标签/搜索