Redis 面经

1 缓存有哪些类型?

  1. 本地缓存:本地缓存就是在进程的内存中进行缓存。本地缓存是内存访问,没有远程交互开销,性能最好,但是受限於单机容量,一般缓存较小且无法扩展。
  2. 分布式缓存:分布式缓存一般都具有良好的水平扩展能力,对较大数据量的场景也能应付自如。缺点就是需要进行远程请求,性能不如本地缓存。
  3. 多级缓存:为了平衡这种情况,实际业务中一般采用多级缓存,本地缓存只保存访问频率最高的部分热点数据,其他的热点数据放在分布式缓存中。

2 为什么我们需要使用 Redis?

传统的关系型数据库如 Mysql 已经不能适用所有的场景了,比如秒杀的库存扣减,APP首页的访问流量高峰等等,都很容易把数据库打崩,所以引入了缓存中间件 Redis。

3 Redis 为什么这么快?

  1. 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速
  2. 数据结构简单,对数据操作也简单
  3. 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
  4. 使用多路 I/O 复用模型,非阻塞 IO

4 Memcached 的优点与缺点

Memcache 的优点如下:

  1. Memcached 处理请求时使用多线程异步 IO 的方式,可以合理利用 CPU 多核的优势,性能非常优秀;
  2. Memcached 功能简单,使用内存存储数据;
  3. Memcached 对缓存的数据可以设置失效期,过期后的数据会被清除;
  4. 失效的策略采用延迟失效,就是当再次使用数据时检查是否失效;
  5. 当容量存满时,会对缓存中的数据进行剔除,剔除时除了会对过期 key 进行清理,还会按 LRU 策略对数据进行剔除。

Memcache 的缺点如下:

  1. key 不能超过 250 个字节;
  2. value 不能超过 1M 字节;
  3. key 的最大失效时间是 30 天;
  4. 只支持 K-V 结构,不提供持久化和主从同步功能。

5 Redis 和 Memcached 的区别?

  1. 可靠性:Redis 支持持久化,有快照和 AOF 两种方式,而 Memcached 是纯的内存存储,不支持持久化的。
  2. 数据结构:Memcached 仅仅支持 string,而 Redis 支持 string,list,set,sorted set,hash。
  3. value 大小:Redis 最大可以达到 1GB,而 Memcached 只有 1MB。
  4. 线程:Memcached 使用多线程,主线程 listen,多个 worker 子线程执行读写,可能会出现锁冲突。Redis 是单线程的,这样虽然不用考虑锁对插入修改数据造成的时间的影响,但是无法利用多核提高整体的吞吐量,只能选择多开 Redis 来解决。
  5. 集群:Redis 原生支持集群模式,在 redis3.x 版本中,便能支持 Cluster 模式,而 Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据。
  6. 性能:由于 Redis 只使用单核,而 Memcached 可以使用多核,所以平均每一个核上 Redis 在存储小数据时比 Memcached 性能更高。而在 100k 以上的数据中,Memcached 性能要高于 Redis,虽然 Redis 最近也在存储大数据的性能上进行优化,但是比起 Remcached,还是稍有逊色。

6 Redis 具有的数据结构

  1. String:字符串
  2. Hash:字典
  3. List:列表
  4. Set:集合
  5. SortedSet:有序集合

7 布隆过滤器 (BloomFilter)

Redis-避免缓存穿透的利器之BloomFilter

8 Redis 分布式锁

分布式锁的实现方式

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

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

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

Redis 是单线程的。keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用 scan 指令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用 keys 指令长。

注意:不过,增量式迭代命令也不是没有缺点的: 举个例子, 使用 SMEMBERS 命令可以返回集合键当前包含的所有元素, 但是对于 SCAN 这类增量式迭代命令来说, 因为在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证 。

11 Redis 是怎么持久化的?

RDB 做镜像全量持久化,AOF 做增量持久化。因为 RDB 会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要 AOF 来配合使用。在 Redis 实例重启时,会使用 RDB 持久化文件重新构建内存,再使用 AOF 重放近期的操作指令来实现完整恢复重启之前的状态。

这里很好理解,把 RDB 理解为一整个表全量的数据,AOF 理解为每次操作的日志就好了,服务器重启的时候先把表的数据全部搞进去,但是他可能不完整,你再回放一下日志,数据不就完整了嘛。不过 Redis 本身的机制是 AOF 持久化开启且存在 AOF 文件时,优先加载 AOF 文件;AOF 关闭或者 AOF 文件不存在时,加载 RDB 文件;加载 AOF/RDB 文件城后,Redis 启动成功; AOF/RDB 文件存在错误时,Redis 启动失败并打印错误信息

12 如果突然机器掉电,Redis 是如何实现持久化的?

取决于 AOF 日志 sync 属性的配置,如果不要求性能,在每条写指令时都 sync 一下磁盘,就不会丢失数据。但是在高性能的要求下每次都 sync 是不现实的,一般都使用定时 sync,比如1s 1次,这个时候最多就会丢失1s的数据。

13 淘汰策略

由于成本和内存限制,当存储的数据超过缓存容量时,需要对缓存的数据进行剔除。一般的剔除策略有以下三种:

  1. FIFO 淘汰最早数据
  2. LRU 剔除最近最少使用
  3. LFU 剔除最近使用频率最低的数据

14 什么是 RDB 持久化?

RDB 持久化是默认的持久化方式,在指定的时间间隔内将内存中的数据集快照写入磁盘。其实就是将内存中数据以快照的方式写入到二进制文件中。Redis 在进行数据持久化的过程中,会先将数据写入到一个临时文件中,待持久化过程都结束了,才会用这个临时文件替换上次持久化好的文件。正是这种特性,让我们可以随时来进行备份,因为快照文件总是完整可用的。

对于 RDB 方式,Redis 会单独创建(fork)一个子进程来进行持久化,而主进程是不会进行任何 IO 操作的,这样就确保了 Redis 极高的性能。

15 什么是 AOF 持久化?

AOF,即 Append Only File,只允许追加不允许改写的文件。其实 AOF 方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行一遍。

默认的 AOF 持久化策略是每秒钟 fsync 一次(fsync 是指把缓存中的写指令记录到磁盘中),因为在这种情况下,Redis 仍然可以保持很好的处理性能,即使 Redis 故障,也只会丢失最近1秒钟的数据。

因为采用了追加方式,如果不做任何处理的话,AOF 文件会变得越来越大,为此,Redis 提供了 AOF 文件重写(rewrite)机制,即当 AOF 文件的大小超过所设定的阈值时,Redis 就会启动 AOF 文件的内容压缩,只保留可以恢复数据的最小指令集。

在进行 AOF 重写时,仍然是采用先写临时文件,全部完成后再替换的流程,所以断电、磁盘满等问题都不会影响 AOF 文件的可用性。

16 RDB 持久化与 AOF 持久化的优缺点对比?

Redis(2)–持久化,主从同步

17 RDB 和 AOF 两者怎么选择?

单独用 RDB 会丢失很多数据,单独用 AOF 会导致数据恢复没 RDB 快,当真出什么问题时第一时间用 RDB 恢复,然后 AOF 做数据补全即可。

18 Redis 的同步机制?

Redis 可以使用主从同步,从从同步。第一次同步时,主节点做一次 bgsave,并同时将后续修改操作记录到内存 buffer,待完成后将 RDB 文件全量同步到复制节点,复制节点接受完成后将 RDB 镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。后续的增量数据通过 AOF 日志同步即可,有点类似数据库的 binlog。

19 Redis 集群?

  1. Redis Sentinal 着眼于高可用,在 master 宕机时会自动将 slave 提升为 master,继续提供服务。
  2. Redis Cluster 着眼于扩展性,在单个 Redis 内存不足时,使用 Cluster 进行分片存储。

20 什么是缓存雪崩?

对热点数据我们一般会采用缓存,但是缓存一般是由定时任务去刷新的,可能会导致缓存的所有 Key 在同一时间同时失效,这会导致所有请求全部打到数据库上去,将会导致灾难性的结果。

21 如何解决缓存雪崩的问题?

在批量往 Redis 存数据的时候,把每个 Key 的失效时间都加个随机值,保证数据不会在同一时间大面积失效。或者设置热点数据永远不过期,有更新操作就更新缓存就好了。

22 什么是缓存穿透?

用户不断向缓存和数据库中都没有的数据发起请求,例如,数据库 id 一般是从1自增上去的,如果用户一直申请 id 为 -1 的数据,每次都能绕开 Redis 直接打到数据库,数据库也查不到,很可能会导致数据库压力过大,严重的甚至会击垮数据库。

23 如何解决缓存穿透的问题?

  1. 在接口层增加校验,比如用户鉴权校验,参数做校验,不合法的参数直接代码 Return,比如:id 做基础校验,id <=0 的直接拦截等。
  2. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将对应 Key 的 Value 对写为 null、位置错误、稍后重试这样的值,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。
  3. 为了防止攻击用户反复用同一个 id 暴力攻击,可以在网关层 Nginx 中对每秒访问次数超出阈值的 IP 进行拉黑处理。
  4. 使用布隆过滤器,利用高效的数据结构和算法快速判断出 Key 是否在数据库中存在,不存在直接 return 就好,存在就去查数据库刷新缓存再 return。

24 什么是缓存击穿?

缓存击穿与缓存雪崩类似,又有一些不同。缓存雪崩是因为大面积的缓存失效打崩了数据库,而缓存击穿是指一个 Key 非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个 Key 在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞。

25 如何解决缓存击穿的问题?

设置热点数据永远不过期。或者加上互斥锁即可。

26 Redis 与关系型数据库的区别?

在这里插入图片描述

27 服务器是多核的,Redis 却是单线程的,会不会造成浪费?

可以在单机开多个 Redis 实例。

28 单机瓶颈问题如何解决?

使用集群的部署方式 Redis cluster,并且是主从同步读写分离,类似 Mysql 的主从同步,Redis cluster 支撑 N 个 Redis master node,每个 master node 都可以挂载多个 slave node。

这样整个 Redis 就可以横向扩容了。如果你要支撑更大数据量的缓存,那就横向扩容更多的 master 节点,每个 master 节点就能存放更多的数据了。

29 Redis 的哨兵模式?

Redis(3)–哨兵模式,集群

30 Redis 为什么需要主从的架构模式?

单机 QPS (每秒内查询次数)是有上限的,而且 Redis 的特性就是必须支撑读高并发的,那一台机器又读又写,很容易带来性能的问题。但如果只让这个 master 机器去写,数据同步给别的 slave 机器,他们都拿去读,分发掉大量的请求那是不是好很多,而且扩容的时候还可以轻松实现水平扩容。
在这里插入图片描述

31 主从同步是如何实现的?

启动一台 slave 的时候,他会发送一个 psync 命令给 master ,如果是这个 slave 第一次连接到 master,他会触发一个全量复制。master 就会启动一个线程,生成 RDB 快照,还会把新的写请求都缓存在内存中,RDB 文件生成后,master 会将这个 RDB 发送给 slave 的,slave 拿到之后做的第一件事情就是写进本地的磁盘,然后加载进内存,然后 master 会把内存里面缓存的那些新命令都发给 slave。

32 Redis 的过期策略?

  1. 定期删除:一段时间内随机抽一些设置了过期时间的 key,检查是否过期,过期就删了
  2. 惰性删除:不主动删除,当查询来了时检测是否过期,过期就删除不返回,没过期就像原来一样即可。

33 Redis 中的事务?

Redis 提供的不是严格的事务,Redis 只保证串行执行命令,并且能保证全部执行,但是执行命令失败时并不会回滚,而是会继续执行下去。

34 Redis 的高级用法 HyperLogLog?

提供不精确的去重计数功能,比较适合用来做大规模数据的去重统计,例如统计 UV。

35 String 的使用场景?

  1. 缓存功能:String 字符串是最常用的数据类型,不仅仅是 Redis,各个语言都是最基本类型,因此,利用 Redis 作为缓存,配合其它数据库作为存储层,利用 Redis 支持高并发的特点,可以大大加快系统的读写速度、以及降低后端数据库的压力。
  2. 计数器:许多系统都会使用 Redis 作为系统的实时计数器,可以快速实现计数和查询的功能。而且最终的数据结果可以按照特定的时间落地到数据库或者其它存储介质当中进行永久保存。
  3. 共享用户 Session:用户重新刷新一次界面,可能需要访问一下数据进行重新登录,或者访问页面缓存 Cookie,但是可以利用 Redis 将用户的 Session 集中管理,在这种模式只需要保证 Redis 的高可用,每次用户 Session 的更新和获取都可以快速完成。大大提高效率。

36 Hash 的使用场景?

这个是类似 Map 的一种结构,这个一般就是可以将结构化的数据,比如一个对象(前提是这个对象没嵌套其他的对象)给缓存在 Redis 里,然后每次读写缓存的时候,可以就操作 Hash 里的某个字段。

但是这个的场景其实还是多少单一了一些,因为现在很多对象都是比较复杂的,比如商品对象可能里面就包含了很多属性,其中也有对象。

37 List 的使用场景?

  1. 消息队列:Redis 的链表结构,可以轻松实现阻塞队列,可以使用左进右出的命令组成来完成队列的设计。比如:数据的生产者可以通过 Lpush 命令从左边插入数据,多个数据消费者,可以使用 BRpop 命令阻塞的“抢”列表尾部的数据。
  2. 文章列表或者数据分页展示的应用。比如,我们常用的博客网站的文章列表,当用户量越来越多时,而且每一个用户都有自己的文章列表,而且当文章多时,都需要分页展示,这时可以考虑使用 Redis 的列表,列表不但有序同时还支持按照范围内获取元素,可以完美解决分页查询功能。大大提高查询效率。

38 Set 的使用场景?

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

可以基于 Set 玩交集、并集、差集的操作,比如交集吧,我们可以把两个人的好友列表整一个交集,看看俩人的共同好友是谁。

39 Sorted Set 的使用场景?

Sorted set 是排序的 Set,去重但可以排序,写进去的时候给一个分数,自动根据分数排序。

  1. 排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等。
  2. 用 Sorted Sets 来做带权重的队列,比如普通消息的 score 为1,重要消息的 score 为2,然后工作线程可以选择按 score 的倒序来获取工作任务。让重要的任务优先执行。

40 如果多个系统同时操作(并发)Redis 带来的数据问题?如何解决?

在这里插入图片描述
A、B、C 三个系统,分别去操作 Redis 的同一个 Key,本来顺序是1,2,3是正常的,但是因为系统 A 网络突然抖动了一下,B,C 在他前面操作了 Redis,导致出错。

解决方法:我们可以使用 Zookeeper 帮助我们管理数据。

在这里插入图片描述
某个时刻,多个系统实例都去更新某个 key。可以基于 Zookeeper 实现分布式锁。每个系统通过 Zookeeper 获取分布式锁,确保同一时间,只能有一个系统实例在操作某个 Key,别人都不允许读和写。

要写入缓存的数据,都是从 MySQL 里查出来的,都得写入 MySQL 中,写入 MySQL 中的时候必须保存一个时间戳,从 MySQL 查出来的时候,时间戳也查出来。

每次要写之前,先判断一下当前这个 Value 的时间戳是否比缓存里的 Value 的时间戳要新。如果是的话,那么可以写,否则,就不能用旧的数据覆盖新的数据。

41 缓存与数据库的双写一致性问题?

数据库和缓存的双写一致性问题.

42 Cache Aside Pattern 为什么是删除缓存,而不是更新缓存?

Cache Aside Pattern 是最经典的缓存+数据库读写的模式,读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。更新的时候,先更新数据库,然后再删除缓存。

至于为什么是删除缓存,而不是更新缓存?原因是更新缓存的代价有时候是很高的。如果你频繁修改一个缓存涉及的多个表,缓存也频繁更新,但这个缓存不会被频繁访问到,岂不是会有大量的冷数据?在这种情况下,用到缓存才去算缓存,会使开销大幅度降低。说到底,这是一种 Lazy 计算的思想,不要每次都重新做复杂的计算,不管它会不会用到,而是让它到需要被使用的时候再重新计算。

43 Redis 的线程模型?

Redis 内部使用文件事件处理器 file event handler,这个文件事件处理器是单线程的,所以 Redis 才叫做单线程的模型。它采用 IO 多路复用机制同时监听多个 Socket,根据 Socket 上的事件来选择对应的事件处理器进行处理。

文件事件处理器的结构包含 4 个部分:

  1. 多个 Socket
  2. IO 多路复用程序
  3. 文件事件分派器
  4. 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)

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

参考:《吊打面试官》系列-Redis基础知识
《吊打面试官》系列-缓存雪崩、击穿、穿透
《吊打面试官》系列-Redis哨兵、持久化、主从、手撕LRU
《吊打面试官》系列-Redis双写一致性、并发竞争、线程模型
《吊打面试官》系列-Redis常见面试题(带答案)
redis和Memcached的区别,都什么时候使用?

发布了123 篇原创文章 · 获赞 226 · 访问量 2万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章