超详细的Redis面试题目(持续更新)

1. 什么是 Redis?

Redis 本质上是一个 Key-Value 类型的内存数据库,很像 memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据 flush 到硬盘上进行保存。因为是纯内存操作,Redis 的性能非常出色,每秒可以处理超过 10 万次读写操作,是已知性能最快的 Key-Value DB。 Redis 的出色之处不仅仅是性能,Redis 最大的魅力是支持保存多种数据结构,此外单个 value 的最大限制是 1GB,不像 memcached 只能保存 1MB 的数据,因此 Redis 可以用来实现很多有用的功能,比方说用他的 List 来做 FIFO 双向链表,实现一个轻量级的高性 能消息队列服务,用他的 Set 可以做高性能的 tag 系统等等。另外 Redis 也可以对存入的 Key-Value 设置 expire 时间,因此也可以被当作一 个功能加强版的 memcached 来用。 Redis 的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此 Redis 适合的场景主要局限在较小数据量的高性能操作和运算上。


2. Redis 有哪些数据结构?

  • String 字符串:string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如 jpg 图片或者序列化的对象 。string 类型是 Redis 最基本的数据类型,一个键最大能存储 512MB。

  • Hash(哈希):Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。

  • List(列表):Redis 列表是简单的字符串列表,按照插入顺序排序。

  • Set(集合):Redis 的 Set 是 string 类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O (1)。

  • zset (sorted set:有序集合):Redis zset 和 set 一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。


3. Redis 为什么是单线程的?

因为 CPU 不是 Redis 的瓶颈。Redis 的瓶颈最有可能是机器内存或者网络带宽。(以上主要来自官方 FAQ)既然单线程容易实现,而且 CPU 不会成为瓶颈,那就顺理成章地采用单线程的方案了。

关于Redis的线程安全:

  • redis是单线程运行,所以多个redis命令是一个一个执行,所以是线程安全的
  • 但是分开的两个redis命令,对于【应用】不是线程安全的,因为这两个redis命令之间会有其他命令,就像java线程不安全的i++操作,这个两个redis命令没有事务管理
  • 可以用RPOPLPUSH, 或者lua脚本,实现多个redis操作合为一个命令,这样就对【应用】线程安全了
  • 利用队列技术将并行访问变为串行访问,消除了传统数据库并发访问控制锁的开销

4. 使用 Redis 有哪些好处?

(1) 速度快,因为数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是 O (1)
(2) 支持丰富数据类型,支持 string,list,set,sorted set,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按 key 设置过期时间,过期后将会自动删除


5. Redis 相比 memcached 有哪些优势?

(1) memcached 所有的值均是简单的字符串,redis 作为其替代者,支持更为丰富的数据类型
(2) redis 的速度比 memcached 快很多
(3) redis 可以持久化其数据
(4) Redis 支持数据的备份,即 master-slave 模式的数据备份
(5) 使用底层模型不同
它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。
Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
(6)value 大小:redis 最大可以达到 1GB,而 memcache 只有 1MB


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

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

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

set 指令有非常复杂的参数,这个应该是可以同时把 setnx 和 expire 合成一条指令来用的!


7. Redis 如何做持久化的?

Redis有两种持久化方式:

  • RDB 持久化:redis 提供了 RDB 持久化的功能,这个功能可以将 redis 在内存中的的状态保存到硬盘中,它可以手动执行,也可以再 redis.conf 中配置,定期执行。RDB 持久化产生的 RDB 文件是一个经过压缩的二进制文件,这个文件被保存在硬盘中,redis 可以通过这个文件还原数据库当时的状态。

  • AOF 持久化:AOF 持久化(Append-Only-File),与 RDB 持久化不同,AOF 持久化是通过保存 Redis 服务器锁执行的写状态来记录数据库的。具体来说,RDB 持久化相当于备份数据库状态,而 AOF 持久化是备份数据库接收到的命令,所有被写入 AOF 的命令都是以 redis 的协议格式来保存的。在 AOF 持久化的文件中,数据库会记录下所有变更数据库状态的命令,除了指定数据库的 select 命令,其他的命令都是来自 client 的,这些命令会以追加 (append) 的形式保存到文件中。

  • 对比:
    AOF 更安全,可将数据及时同步到文件中,但需要较多的磁盘 IO,AOF 文件尺寸较大,文件内容恢复相对较慢,也更完整。
    RDB 持久化,安全性较差,它是正常时期数据备份及 master-slave 数据同步的最佳手段,文件尺寸较小,恢复数度较快。

RDB的实现方式有两种:SAVE和BGSAVE

  • SAVE 直接调用 rdbSave ,阻塞 Redis 主进程,直到保存完成为止。在主进程阻塞期间,服务器不能处理客户端的任何请求。
  • BGSAVE 则 fork 出一个子进程,子进程负责调用 rdbSave ,并在保存完成之后向主进程发送信号,通知保存已完成。 Redis 服务器在 BGSAVE 执行期间仍然可以继续处理客户端的请求。BGSAVE 命令执行之后立即返回 OK ,然后 Redis fork 出一个新子进程,原来的 Redis 进程 (父进程) 继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。

如果突然机器掉电AOF持久化会怎样?

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

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

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

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

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


9.如果有大量的 key 需要设置同一时间过期,一般需要注意什么?

如果大量的 key 过期时间设置的过于集中,到过期的那个时间点,redis 可能会出现短暂的卡顿现象(缓存雪崩)。一般需要在时间上加一个随机值,使得过期时间分散一些。


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

一般的缓存系统,都是按照 key 去缓存查询,如果不存在对应的 value,就应该去后端系统查找(比如 DB)。一些恶意的请求会故意查询不存在的 key, 请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。

如何避免?

  • 对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该 key 对应的数据 insert 了之后清理缓存。

  • 对一定不存在的 key 进行过滤。可以把所有的可能存在的 key 放到一个大的 Bitmap 中,查询时通过该 bitmap 过滤。

缓存击穿:

在高并发下,对一个特定的值进行查询,但是这个时候缓存正好过期了,缓存没有命中,导致大量请求直接落到数据库上,如活动系统里面查询活动信息,但是在活动进行过程中活动缓存突然过期了。

避免:

  • 后台定义一个 job (定时任务) 专门主动更新缓存数据。比如,一个缓存中的数据过期时间是 30 分钟,那么 job 每隔 29 分钟定时刷新数据 (将从数据库中查到的数据更新到缓存中).
  • 将缓存 key 的过期时间 (绝对时间) 一起保存到缓存中 (可以拼接,可以添加新字段,可以采用单独的 key 保存… 不管用什么方式,只要两者建立好关联关系就行). 在每次执行 get 操作后,都将 get 出来的缓存过期时间与当前系统时间做一个对比,如果缓存过期时间 - 当前系统时间 <=1 分钟 (自定义的一个值), 则主动更新缓存。这样就能保证缓存中的数据始终是最新的 (和方案一一样,让数据不过期.)
  • 采用 L1 (一级缓存) 和 L2 (二级缓存) 缓存方式,L1 缓存失效时间短,L2 缓存失效时间长。 请求优先从 L1 缓存获取数据,如果 L1 缓存未命中则加锁,只有 1 个线程获取到锁,这个线程再从数据库中读取数据并将数据再更新到到 L1 缓存和 L2 缓存中,而其他线程依旧从 L2 缓存获取数据并返回。

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

当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。

如何避免?

1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 只允许一个线程查询数据和写缓存,其他线程等待。

2:做二级缓存,A1 为原始缓存,A2 为拷贝缓存,A1 失效时,可以访问 A2,A1 缓存失效时间设置为短期,A2 设置为长期

3:不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀。


12.Redis 有哪些架构模式?讲讲各自的特点

(1)单机版

特点:内存容量有限 、处理能力有限、无法高可用。

(2)主从复制、读写分离

Redis 的复制(replication)功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(master),而通过复制创建出来的服务器复制品则为从服务器(slave)。 只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步 给从服务器,从而一直保证主从服务器的数据相同。

特点:master/slave 数据相同、降低 master 读压力在转交从库

问题:没有解决 master 写的压力

(3)哨兵

Redis sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移。

特点:

  • 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。

  • 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知

  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作。

缺点:没有解决 master 写的压力

(4)集群

特点:

  • 无中心架构(不存在哪个节点影响性能瓶颈)。

  • 数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。

  • 可扩展性,可线性扩展到 1000 个节点,节点可动态添加或删除。

  • 高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做备份数据副本

  • 实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave 到 Master 的角色提升。


13.Redis 为什么这么快?

  • 纯内存数据库,避免了磁盘 I/O 的瓶颈。
  • 单进程单线程,减少了线程上下文切换的开销。
  • 利用队列技术将并行访问变为串行访问,消除了传统数据库并发访问控制锁的开销。
  • Redis 全程使用 hash 结构,读取速度快,还有一些特殊的数据结构,对数据存储进行了优化,如压缩表,对短数据进行压缩存储,再如,跳表,使用有序的数据结构加快读取的速度。
  • 使用多路 I/O 复用模型,非阻塞 I/O。

多路 I/O 复用模型:

  • 多路 I/O 复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。
  • 这里 “多路” 指的是多个网络连接,“复用” 指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响 Redis 性能的瓶颈。

14.AOF持久化方式存储的内容,格式是什么?

(1)在 AOF 持久化的文件中,数据库会记录下所有变更数据库状态的命令

内容是 redis 通讯协议 (RESP ) 格式的命令文本存储。

(2)什么是 RESP?有什么特点?

RESP 是 redis 客户端和服务端之前使用的一种通讯协议;

RESP 的特点:实现简单、快速解析、可读性好

  • For Simple Strings the first byte of the reply is “+” 回复

  • For Errors the first byte of the reply is “-” 错误

  • For Integers the first byte of the reply is “:” 整数

  • For Bulk Strings the first byte of the reply is “$” 字符串

  • For Arrays the first byte of the reply is “*” 数组


15.Redis 常见的性能问题和解决方法?

  • Master写内存快照:save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。
  • Master AOF持久化:如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。
  • Redis主从复制的性能问题:第一次Slave向Master同步的实现是:Slave向Master发出同步请求,Master先dump出rdb文件,然后将rdb文件全量传输给slave,然后Master把缓存的命令转发给Slave,初次同步完成。第二次以及以后的同步实现是:Master将变量的快照直接实时依次发送给各个Slave。不管什么原因导致Slave和Master断开重连都会重复以上过程。Redis的主从复制是建立在内存快照的持久化基础上,只要有Slave就一定会有内存快照发生。虽然Redis宣称主从复制无阻塞,但由于Redis使用单线程服务,如果Master快照文件比较大,那么第一次全量传输会耗费比较长时间,且文件传输过程中Master可能无法提供服务,也就是说服务会中断,对于关键服务,这个后果也是很可怕的。
  • 根本问题的原因都离不开系统io瓶颈问题,也就是硬盘读写速度不够快,主进程 fsync()/write() 操作被阻塞。

16.Redis淘汰策略

大家可以想想,当我们的物理服务内存只有8G,但是数据却有100G,如何慢慢的放在redis中呢?再加上公司近期资金不够,不能扩展机器怎么办?就需要淘汰已有数据

  • voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
  • volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  • volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  • allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
  • allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  • no-enviction(驱逐):禁止驱逐数据

17.redis 有哪些功能?

  • 全页面缓存:整页缓存。如果你正在使用服务器端呈现的内容,则不需要为每个单独的请求重新渲染每个页面。使用如Redis这样的缓存,你可以缓存经常请求的内容,从而大大减少请求最多的页面的延迟,并且大多数框架针对Redis缓存页面都有hooks。

  • 顺序排列:由于Redis在内存中Set数据结构可以非常快速和高效进行递增和递减,性能远远高于SQL查询。比较这与Redis的排序集相结合意味着你可以以毫秒为单位抓取列表中评分最高的项目,而且实现起来非常容易。

    • 利用zset类型可以存储排行榜

    • 利用list的自然时间排序存储最新n个数据

  • 会话Session存储:Redis最常见的用途是会话存储。与其他会话存储(如Memcache)不同,Redis可以保留数据,以便在缓存停止的情况下,在重新启动时,所有数据仍然存在。

  • 队列:使用Redis可以做的一个不太常见,但非常有用的事情是排队。无论是电子邮件队列还是其他应用程序使用的数据,你都可以在Redis中创建一个高效的队列。任何熟悉堆栈以及会push和pop项目的开发人员都可以轻松自然地使用此功能。

  • 配合关系型数据库做高速缓存


【Java 面试那点事】

这里致力于分享 Java 面试路上的各种知识,无论是技术还是经验,你需要的这里都有!

这里可以让你【快速了解 Java 相关知识】,并且【短时间在面试方面有跨越式提升】

面试路上,你不孤单!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员世杰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值