乐趣区

关于java:Redis基础面试题2021最新版

大家好,我是库森。

本文收录于《面试小抄》系列,Github 地址(可下载 pdf):https://github.com/cosen1024/… 国内 Gitee(可下载 pdf):https://gitee.com/cosen1024/J…

Redis 作为现在最火的 NoSQL 数据库,在大厂面试中呈现的频率越来越高,把握 Redis 也逐步成为后端开发工程师的必备技能。

Redis 学的好,也会成为面试的加分利器,库森校招时,Redis 就是加分利器,好屡次和面试官聊 Redis 都超过半小时!

为帮忙小伙伴们更好温习,我将连载几期 Redis 面试文章。这次带来的是 Redis 根底的面试题,先点赞和珍藏再看吧~

老规矩,先上目录~

1. Redis 是什么?简述它的优缺点?

Redis 实质上是一个 Key-Value 类型的内存数据库,很像 Memcached,整个数据库加载在内存当中操作,定期通过异步操作把数据库中的数据 flush 到硬盘上进行保留。

因为是纯内存操作,Redis 的性能十分杰出,每秒能够解决超过 10 万次读写操作,是已知性能最快的 Key-Value 数据库。

长处

  • 读写性能极高,Redis 能读的速度是 110000 次 /s,写的速度是 81000 次 /s。
  • 反对数据长久化,反对 AOF 和 RDB 两种长久化形式。
  • 反对事务,Redis 的所有操作都是原子性的,意思就是要么胜利执行要么失败齐全不执行。单个操作是原子性的。多个操作也反对事务,即原子性,通过 MULTI 和 EXEC 指令包起来。
  • 数据结构丰盛,除了反对 string 类型的 value 外,还反对 hash、set、zset、list 等数据结构。
  • 反对主从复制,主机会主动将数据同步到从机,能够进行读写拆散。
  • 丰盛的个性 – Redis 还反对 publish/subscribe,告诉,key 过期等个性。

毛病

  • 数据库容量受到物理内存的限度,不能用作海量数据的高性能读写,因而 Redis 适宜的场景次要局限在较小数据量的高性能操作和运算上。

2. Redis 为什么这么快?

  • 内存存储:Redis 是应用内存 (in-memeroy) 存储,没有磁盘 IO 上的开销。数据存在内存中,相似于 HashMap,HashMap 的劣势就是查找和操作的工夫复杂度都是 O(1)。
  • 单线程实现(Redisv6.0 以前):Redis 应用单个线程解决申请,防止了多个线程之间线程切换和锁资源争用的开销。留神:单线程是指的是在外围网络模型中,网络申请模块应用一个线程来解决,即一个线程解决所有网络申请。
  • 非阻塞 IO:Redis 应用多路复用 IO 技术,将 epoll 作为 I / O 多路复用技术的实现,再加上 Redis 本身的事件处理模型将 epoll 中的连贯、读写、敞开都转换为事件,不在网络 I / O 上节约过多的工夫。
  • 优化的数据结构:Redis 有诸多能够间接利用的优化数据结构的实现,应用层能够间接应用原生的数据结构晋升性能。
  • 应用底层模型不同:Redis 间接本人构建了 VM (虚拟内存)机制,因为个别的零碎调用零碎函数的话,会节约肯定的工夫去挪动和申请。

    Redis 的 VM(虚拟内存)机制就是临时把不常常拜访的数据 (冷数据) 从内存替换到磁盘中,从而腾出贵重的内存空间用于其它须要拜访的数据(热数据)。通过 VM 性能能够实现冷热数据拆散,使热数据仍在内存中、冷数据保留到磁盘。这样就能够防止因为内存不足而造成访问速度降落的问题。

    Redis 进步数据库容量的方法有两种:一种是能够将数据宰割到多个 RedisServer 上;另一种是应用虚拟内存把那些不常常拜访的数据交换到磁盘上。须要特地留神的是 Redis 并没有应用 OS 提供的 Swap,而是本人实现。

3. Redis 相比 Memcached 有哪些劣势?

  • 数据类型:Memcached 所有的值均是简略的字符串,Redis 反对更为丰盛的数据类型,反对 string(字符串),list(列表),Set(汇合)、Sorted Set(有序汇合)、Hash(哈希)等。
  • 长久化:Redis 反对数据落地长久化存储,能够将内存中的数据放弃在磁盘中,重启的时候能够再次加载进行应用。memcache 不反对数据长久存储。
  • 集群模式:Redis 提供主从同步机制,以及 Cluster 集群部署能力,可能提供高可用服务。Memcached 没有原生的集群模式,须要依附客户端来实现往集群中分片写入数据
  • 性能比照:Redis 的速度比 Memcached 快很多。
  • 网络 IO 模型:Redis 应用单线程的多路 IO 复用模型,Memcached 应用多线程的非阻塞 IO 模式。
  • Redis 反对服务器端的数据操作:Redis 相比 Memcached 来说,领有更多的数据结构和并反对更丰盛的数据操作,通常在 Memcached 里,你须要将数据拿到客户端来进行相似的批改再 set 回去。

    这大大增加了网络 IO 的次数和数据体积。在 Redis 中,这些简单的操作通常和个别的 GET/SET 一样高效。所以,如果须要缓存可能反对更简单的构造和操作,那么 Redis 会是不错的抉择。

4. Redis 的罕用场景有哪些?

1、缓存

缓存当初简直是所有中大型网站都在用的必杀技,正当的利用缓存不仅可能晋升网站访问速度,还能大大降低数据库的压力。Redis 提供了键过期性能,也提供了灵便的键淘汰策略,所以,当初 Redis 用在缓存的场合十分多。

2、排行榜

很多网站都有排行榜利用的,如京东的月度销量榜单、商品按工夫的上新排行榜等。Redis 提供的有序汇合数据类构能实现各种简单的排行榜利用。

3、计数器

什么是计数器,如电商网站商品的浏览量、视频网站视频的播放数等。为了保证数据实时效,每次浏览都得给 +1,并发量高时如果每次都申请数据库操作无疑是种挑战和压力。Redis 提供的 incr 命令来实现计数器性能,内存操作,性能十分好,十分实用于这些计数场景。

4、分布式会话

集群模式下,在利用不多的状况下个别应用容器自带的 session 复制性能就能满足,当利用增多绝对简单的零碎中,个别都会搭建以 Redis 等内存数据库为核心的 session 服务,session 不再由容器治理,而是由 session 服务及内存数据库治理。

5、分布式锁

在很多互联网公司中都应用了分布式技术,分布式技术带来的技术挑战是对同一个资源的并发拜访,如全局 ID、减库存、秒杀等场景,并发量不大的场景能够应用数据库的乐观锁、乐观锁来实现,但在并发量高的场合中,利用数据库锁来管制资源的并发拜访是不太现实的,大大影响了数据库的性能。能够利用 Redis 的 setnx 性能来编写分布式的锁,如果设置返回 1 阐明获取锁胜利,否则获取锁失败,理论利用中要思考的细节要更多。

6、社交网络

点赞、踩、关注 / 被关注、独特好友等是社交网站的基本功能,社交网站的访问量通常来说比拟大,而且传统的关系数据库类型不适宜存储这种类型的数据,Redis 提供的哈希、汇合等数据结构能很不便的的实现这些性能。如在微博中的独特好友,通过 Redis 的 set 可能很不便得出。

7、最新列表

Redis 列表构造,LPUSH 能够在列表头部插入一个内容 ID 作为关键字,LTRIM 可用来限度列表的数量,这样列表永远为 N 个 ID,无需查问最新的列表,间接依据 ID 去到对应的内容页即可。

8、音讯零碎

音讯队列是大型网站必用中间件,如 ActiveMQ、RabbitMQ、Kafka 等风行的音讯队列中间件,次要用于业务解耦、流量削峰及异步解决实时性低的业务。Redis 提供了公布 / 订阅及阻塞队列性能,能实现一个简略的音讯队列零碎。另外,这个不能和业余的消息中间件相比。

5. Redis 的数据类型有哪些?

有五种罕用数据类型:String、Hash、Set、List、SortedSet。以及三种非凡的数据类型:Bitmap、HyperLogLog、Geospatial,其中 HyperLogLog、Bitmap 的底层都是 String 数据类型,Geospatial 的底层是 Sorted Set 数据类型。

五种罕用的数据类型

1、String:String 是最罕用的一种数据类型,一般的 key- value 存储都能够归为此类。其中 Value 既能够是数字也能够是字符串。应用场景:惯例 key-value 缓存利用。惯例计数: 微博数,粉丝数。

2、Hash:Hash 是一个键值 (key => value) 对汇合。Redishash 是一个 string 类型的 field 和 value 的映射表,hash 特地适宜用于存储对象,并且能够像数据库中 update 一个属性一样只批改某一项属性值。

3、Set:Set 是一个无序的人造去重的汇合,即 Key-Set。此外还提供了交加、并集等一系列间接操作汇合的办法,对于求独特好友、独特关注什么的性能实现特地不便。

4、List:List 是一个有序可反复的汇合,其遵循 FIFO 的准则,底层是依赖双向链表实现的,因而反对正向、反向双重查找。通过 List,咱们能够很方面的取得相似于最新回复这类的性能实现。

5、SortedSet:相似于 java 中的 TreeSet,是 Set 的可排序版。此外还反对优先级排序,保护了一个 score 的参数来实现。实用于排行榜和带权重的音讯队列等场景。

三种非凡的数据类型

1、Bitmap:位图,Bitmap 设想成一个以位为单位数组,数组中的每个单元只能存 0 或者 1,数组的下标在 Bitmap 中叫做偏移量。应用 Bitmap 实现统计性能,更省空间。如果只须要统计数据的二值状态,例如商品有没有、用户在不在等,就能够应用 Bitmap,因为它只用一个 bit 位就能示意 0 或 1。

2、Hyperloglog。HyperLogLog 是一种用于统计基数的数据汇合类型,HyperLogLog 的长处是,在输出元素的数量或者体积十分十分大

时,计算基数所需的空间总是固定 的、并且是很小的。每个 HyperLogLog 键只须要破费 12 KB 内存,就能够计算靠近 2^64 个不同元素的基 数。场景:统计网页的 UV(即 Unique Visitor,不反复访客,一个人拜访某个网站屡次,然而还是只计算为一次)。

要留神,HyperLogLog 的统计规定是基于概率实现的,所以它给出的统计后果是有肯定误差的,规范误算率是 0.81%。

3、Geospatial:次要用于存储地理位置信息,并对存储的信息进行操作,实用场景如敌人的定位、左近的人、打车间隔计算等。

6. Redis 为何抉择单线程?

在 Redisv6.0 以前,Redis 的外围网络模型抉择用单线程来实现。先来看下官网的答复:

It’s not very frequent that CPU becomes your bottleneck with Redis,as usually Redisis either memory or network bound. For instance,using pipelining Redisrunning on an average Linux system can deliver even 1 million requests per second,so if your application mainly uses O(N) or O(log(N)) commands,it is hardly going to use too much CPU.

外围意思就是,对于一个 DB 来说,CPU 通常不会是瓶颈,因为大多数申请不会是 CPU 密集型的,而是 I/O 密集型。具体到 Redis 的话,如果不思考 RDB/AOF 等长久化计划,Redis 是齐全的纯内存操作,执行速度是十分快的,因而这部分操作通常不会是性能瓶颈,Redis 真正的性能瓶颈在于网络 I/O,也就是客户端和服务端之间的网络传输提早,因而 Redis 抉择了单线程的 I/O 多路复用来实现它的外围网络模型。

实际上更加具体的抉择单线程的起因如下:

  • 防止过多的上下文切换开销:如果是单线程则能够躲避过程内频繁的线程切换开销,因为程序始终运行在过程中单个线程内,没有多线程切换的场景。
  • 防止同步机制的开销:如果 Redis 抉择多线程模型,又因为 Redis 是一个数据库,那么势必波及到底层数据同步的问题,则必然会引入某些同步机制,比方锁,而咱们晓得 Redis 不仅仅提供了简略的 key-value 数据结构,还有 list、set 和 hash 等等其余丰盛的数据结构,而不同的数据结构对同步拜访的加锁粒度又不尽相同,可能会导致在操作数据过程中带来很多加锁解锁的开销,减少程序复杂度的同时还会升高性能。
  • 简略可保护:如果 Redis 应用多线程模式,那么所有的底层数据结构都必须实现成线程平安的,这无疑又使得 Redis 的实现变得更加简单。

总而言之,Redis 抉择单线程能够说是多方博弈之后的一种衡量:在保障足够的性能体现之下,应用单线程放弃代码的简略和可维护性。

7. Redis 真的是单线程?

探讨 这个问题前,先看下 Redis 的版本中两个重要的节点:

  1. Redisv4.0(引入多线程解决异步工作)
  2. Redisv6.0(正式在网络模型中实现 I/O 多线程)

所以,网络上说的 Redis 是单线程,通常是指在 Redisv6.0 之前,其外围网络模型应用的是单线程。而 Redis 的异步工作应用的仍是多线程。

Redis 在 v4.0 版本的时候就曾经引入了的多线程来做一些异步操作,此举次要针对的是那些十分耗时的命令,通过将这些命令的执行进行异步化,防止阻塞单线程的事件循环。

在 Redisv4.0 之后减少了一些的非阻塞命令如 UNLINKFLUSHALL ASYNCFLUSHDB ASYNC

8. Redisv6.0 为何引入多线程?

很简略,就是 Redis 的网络 I/O 瓶颈曾经越来越显著了。

随着互联网的飞速发展,互联网业务零碎所要解决的线上流量越来越大,Redis 的单线程模式会导致系统耗费很多 CPU 工夫在网络 I/O 上从而升高吞吐量,要晋升 Redis 的性能有两个方向:

  • 优化网络 I/O 模块
  • 进步机器内存读写的速度

后者依赖于硬件的倒退,临时无解。所以只能从前者下手,网络 I/O 的优化又能够分为两个方向:

  • 零拷贝技术或者 DPDK 技术
  • 利用多核优势

零拷贝技术有其局限性,无奈齐全适配 Redis 这一类简单的网络 I/O 场景,更多网络 I/O 对 CPU 工夫的耗费和 Linux 零拷贝技术。而 DPDK 技术通过旁路网卡 I/O 绕过内核协定栈的形式又太过于简单以及须要内核甚至是硬件的反对。

因而,利用多核优势成为了优化网络 I/O 性价比最高的计划。

9. Redis 过期键的删除策略?

Redis 的过期删除策略就是:惰性删除和定期删除两种策略配合应用。

惰性删除 :Redis 的惰性删除策略由 db.c/expireIfNeeded 函数实现,所有键读写命令执行之前都会调用 expireIfNeeded 函数对其进行查看,如果过期,则删除该键,而后执行键不存在的操作;未过期则不作操作,继续执行原有的命令。

定期删除:由Redis.c/activeExpireCycle 函数实现,函数以肯定的频率运行,每次运行时,都从肯定数量的数据库中取出肯定数量的随机键进行查看,并删除其中的过期键。

附:删除 key 常见的三种解决形式。

1、定时删除

在设置某个 key 的过期工夫同时,咱们创立一个定时器,让定时器在该过期工夫到来时,立刻执行对其进行删除的操作。

长处:定时删除对内存是最敌对的,可能保留内存的 key 一旦过期就能立刻从内存中删除。

毛病:对 CPU 最不敌对,在过期键比拟多的时候,删除过期键会占用一部分 CPU 工夫,对服务器的响应工夫和吞吐量造成影响。

2、惰性删除

设置该 key 过期工夫后,咱们不去管它,当须要该 key 时,咱们在查看其是否过期,如果过期,咱们就删掉它,反之返回该 key。

长处:对 CPU 敌对,咱们只会在应用该键时才会进行过期查看,对于很多用不到的 key 不用浪费工夫进行过期查看。

毛病:对内存不敌对,如果一个键曾经过期,然而始终没有应用,那么该键就会始终存在内存中,如果数据库中有很多这种应用不到的过期键,这些键便永远不会被删除,内存永远不会开释。从而造成内存透露。

3、定期删除

每隔一段时间,咱们就对一些 key 进行查看,删除外面过期的 key。

长处:能够通过限度删除操作执行的时长和频率来缩小删除操作对 CPU 的影响。另外定期删除,也能无效开释过期键占用的内存。

毛病:难以确定删除操作执行的时长和频率。如果执行的太频繁,定期删除策略变得和定时删除策略一样,对 CPU 不敌对。如果执行的太少,那又和惰性删除一样了,过期键占用的内存不会及时失去开释。另外最重要的是,在获取某个键时,如果某个键的过期工夫曾经到了,然而还没执行定期删除,那么就会返回这个键的值,这是业务不能忍耐的谬误。

10. Redis 内存淘汰机制?

当现有内存大于 maxmemory 时,便会触发 Redis 被动淘汰内存形式,有如下几种淘汰形式:

Redisv4.0 前提供 6 种数据淘汰策略

  • volatile-lru:利用 LRU 算法移除设置过过期工夫的 key (LRU: 最近应用 Least Recently Used)
  • allkeys-lru:当内存不足以包容新写入数据时,在键空间中,移除最近起码应用的 key(这个是最罕用的)
  • volatile-ttl:从已设置过期工夫的数据集(server.db[i].expires)中筛选将要过期的数据淘汰
  • volatile-random:从已设置过期工夫的数据集(server.db[i].expires)中任意抉择数据淘汰
  • allkeys-random:从数据集(server.db[i].dict)中任意抉择数据淘汰
  • no-eviction:禁止驱赶数据,也就是说当内存不足以包容新写入数据时,新写入操作会报错。这个应该没人应用吧!

Redisv4.0 后减少以下两种

  • volatile-lfu:从已设置过期工夫的数据集 (server.db[i].expires) 中筛选最不常常应用的数据淘汰 (LFU(Least Frequently Used) 算法,也就是最频繁被拜访的数据未来最有可能被拜访到)
  • allkeys-lfu:当内存不足以包容新写入数据时,在键空间中,移除最不常常应用的 key。

11. Redis 长久化机制?

为了可能重用 Redis 数据,或者避免系统故障,咱们须要将 Redis 中的数据写入到磁盘空间中,即长久化。Redis 提供了两种不同的长久化办法能够将数据存储在磁盘中,一种叫快照RDB,另一种叫只追加文件AOF

RDB

在指定的工夫距离内将内存中的数据集快照写入磁盘(Snapshot),它复原时是将快照文件间接读到内存里。

劣势:适宜大规模的数据恢复;对数据完整性和一致性要求不高

劣势 :在肯定间隔时间做一次备份,所以如果 Redis 意外down 掉的话,就会失落最初一次快照后的所有批改。

AOF

以日志的模式来记录每个写操作,将 Redis 执行过的所有写指令记录下来(读操作不记录),只许追加文件但不能够改写文件,Redis 启动之初会读取该文件从新构建数据,换言之,Redis 重启的话就依据日志文件的内容将写指令从前到后执行一次以实现数据的复原工作。

AOF 采纳文件追加形式,文件会越来越大,为避免出现此种状况,新增了重写机制,当 AOF 文件的大小超过所设定的阈值时,Redis 就会启动 AOF 文件的内容压缩,只保留能够复原数据的最小指令集.。

劣势

  • 每批改同步:appendfsync always 同步长久化,每次产生数据变更会被立刻记录到磁盘,性能较差但数据完整性比拟好
  • 每秒同步:appendfsync everysec 异步操作,每秒记录,如果一秒内宕机,有数据失落
  • 不同步:appendfsync no 从不同步

劣势

  • 雷同数据集的数据而言 aof 文件要远大于 rdb 文件,复原速度慢于rdb
  • aof运行效率要慢于 rdb,每秒同步策略效率较好,不同步效率和rdb 雷同

如何抉择 RDB 和 AOF

  • 如果是数据不那么敏感,且能够从其余中央从新生成补回的,那么能够敞开长久化。
  • 如果是数据比拟重要,不想再从其余中央获取,且能够接受数分钟的数据失落,比方缓存等,那么能够只应用 RDB。
  • 如果是用做内存数据库,要应用 Redis 的长久化,倡议是 RDB 和 AOF 都开启,或者定期执行 bgsave 做快照备份,RDB 形式更适宜做数据的备份,AOF 能够保证数据的不失落。

Redis4.0 对于长久化机制的优化

Redis4.0 绝对与 3.X 版本其中一个比拟大的变动是 4.0 增加了新的混合长久化形式。

简略的说:新的 AOF 文件前半段是 RDB 格局的全量数据后半段是 AOF 格局的增量数据,如下图:

劣势:混合长久化联合了 RDB 长久化 和 AOF 长久化的长处,因为绝大部分都是 RDB 格局,加载速度快,同时联合 AOF,增量的数据以 AOF 形式保留了,数据更少的失落。

劣势:兼容性差,一旦开启了混合长久化,在 4.0 之前版本都不辨认该 aof 文件,同时因为前局部是 RDB 格局,浏览性较差。

End

更文不易,点赞激励下呗~

我将继续输入干货,与你独特成长~

这里也举荐一个我收集的计算机书籍仓库,仓库目前有上百本经典 cs 电子书,看经典的书籍会更悟得深~

点此链接即可中转书单,计算机必看经典书籍(含 pdf 下载)

Github 也有相应仓库,https://github.com/cosen1024/…
欢送 star。

退出移动版