Redis连环11问

247次阅读  |  发布于3年以前

Redis作为如今最火的NoSQL数据库,在大厂面试中出现的频率越来越高,掌握Redis已成为后端开发工程师的必备技能。

库森校招时,Redis就是杀手锏,好多次和面试官聊Redis都超过半小时,为面试超加分!

库森将连载几期Redis面试文章,这次带来的是Redis基础的面试题,先点赞和收藏再看吧~

老规矩,先上目录~

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

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

因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value 数据库。

优点

缺点

2 . Redis为什么这么快?

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

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选择单线程可以说是多方博弈之后的一种权衡:在保证足够的性能表现之下,使用单线程保持代码的简单和可维护性。

7 . Redis真的是单线程?

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

  1. Redis 4.0(引入多线程处理异步任务)
  2. Redis 6.0(正式在网络模型中实现 I/O 多线程)

所以,网络上说的Redis是单线程,通常是指在Redis 6.0之前,其核心网络模型使用的是单线程;而Redis的异步任务使用的仍是多线程。

Redis在 4.0 版本的时候就已经引入了的多线程来做一些异步操作,此举主要针对的是那些非常耗时的命令,通过将这些命令的执行进行异步化,避免阻塞单线程的事件循环。

在 Redis 4.0 之后增加了一些的非阻塞命令如 UNLINKFLUSHALL ASYNCFLUSHDB ASYNC

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

很简单,就是 Redis的网络 I/O 瓶颈已经越来越明显了。

随着互联网的飞速发展,互联网业务系统所要处理的线上流量越来越大,Redis的单线程模式会导致系统消耗很多 CPU 时间在网络 I/O 上从而降低吞吐量,要提升 Redis的性能有两个方向:

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

零拷贝技术有其局限性,无法完全适配 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主动淘汰内存方式,有如下几种淘汰方式:

Redis 4.0前提供 6种数据淘汰策略

Redis 4.0后增加以下两种

11 . Redis持久化机制?

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

RDB

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

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

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

AOF

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

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

优势

劣势

如何选择RDB和AOF

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

Redis4.0相对与3.X版本其中一个比较大的变化是4.0添加了新的混合持久化方式。

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

优势:混合持久化结合了RDB持久化 和 AOF 持久化的优点, 由于绝大部分都是RDB格式,加载速度快,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。

劣势:兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,阅读性较差

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8