Redis⾼频⾯试题汇总(2021最新版)
本⽂已收录于专栏
上千⼈点赞收藏,全套Redis⾼频⾯试题,⼤⼚必备技能!
⾯试官⼼理分析
从⾯试官的⾓度分析,出这道题的⽬的是为了考察你对缓存的认知⽔平,以及结合缓存处理业务、改善架构的能⼒。这道题很明显是让你⾃由发挥,给了你引领⾯试官往⾃⼰最熟悉的知识点引导的机会,所以要尽可能的把握这次机会,给⾯试官⼀个好的印象。这道题聊得好,就是能深⼊交流个把⼩时了,如果是⼀⾯基本上能轻轻松松拿下。 但是千万不要上来就把话题聊死了,聊太浅了,那基本就是回去等通知了……
⽐如以下这种回答⽅式:
很多⼈会说这么回答也没错呀!没错是没错,但是总有⼀种给你机会不中⽤的感觉。
此时此刻⾯试官内⼼想发⼤致是这样的:
⽐较基础,应该没有很深⼊的了解过Redis
想问题停留在表⾯呀,估计平时就知道⼲活,没想过问题
给你⾃由发挥的机会,你不把握呀,看样⼦还是得我⾃⼰来,先问你⼏个分布式、持久化的问题看看⽔平怎么样,不⾏就这样了吧,后⾯还有好多⼈,等会要下班了。
如果不想硬抗下⾯试官的降龙⼗⼋掌,就应该主动挑起⾯试官的兴趣,并且把⾃⼰的格局(⽔平⼴度和深度)率先提升起来,将⾃⼰会的东西尽可能的多讲⼀些出来。
⽐如以下这种回答⽅式:
Redis⾯试题汇总
⾼性能:⾼性能⼀个很⼤的标准,就是响应时间快。Redis基于内存存储,CPU访问速度快,此外Redis对于数据结构的极致优化、内部线程模型和⽹络I/O模型的设计,决定了Redis是⼀个⾼性能存储数据库。唯⼀的缺点就是内存⽐较昂贵,通常情况下资源⽐较有限,因此对于⾮常⼤的数据缓存架构应该合理设计,我们通常不会在Redis中存放过⼤的数据,因为这样会导致Redis性能下降。
⾼并发:⾼并发通常指标有响应时间(Response Time),吞吐量(Throughput),每秒查询率QPS(Query Per Second)和并发⽤户数等,Redis虽然它是⼀个单进程单线程模型,但是Redis确实⾼并发业务场景下的⼀把利器,⽬前Redis的QPS已经能达到10万甚⾄是100万级别了,这是绝对的⾼并发。
⾼可⽤:Redis⾼可⽤主要体现在主从复制、sentinel(哨兵模式)和Cluster(集模式)三者
这个我的理解⼤致是这样的⾯试官!!
Redis的单线程指的是执⾏命令操作使⽤单线程,Redis6.x发布之后使⽤多线程处理⽹络数据的读写和协议解析,Redis单线程这么快的原因主要有这些点:
采⽤I/O多路复⽤⾮阻塞模型处理客户端socket连接,能够极⼤的优化Redis服务端响应速度和效率,多路 I/O 复⽤技术可以让单个线程⾼效的处理多个连接请求,尽量减少⽹络 IO 的时间消耗。
基于内存存储,减少磁盘IO,读取速度快
Redis对内部数据结构做了极致的优化,使Redis的数据结构⾮常⾼效。并且在不同的数据量和存储内容采取不同的编码⽅式,这就涉及到了 Redis 底层的编码转换。⽐如list、hash、zset 三个键使⽤到了 ziplist 压缩列表编码,ziplist 是⼀种结构紧凑的数据结构,当某⼀键值中所包含的元素较少时,会优先
存储在 ziplist 中,当元素个数超过某⼀值后,才将 ziplist 转化为标准存储结构,当然这⼀值是在f中可⾃定义配置。此外SDS的内存预分配、Hash结果Rehash中的渐进式hash、ZSet顺序基于SkipList存储,都优化了数据结构,使其更快。
Redis在执⾏命令操作使⽤单线程,⽆需考虑并发锁的设计,以及多线程带来的CPU上下⽂切换消耗,因此执⾏命令更快
这个我的理解⼤致是这样的⾯试官!!
Redis有5种基本数据类型它们分别是String、List、Hash、Set、ZSet;此外还有三种特殊数据类型Bitmaps、Geospatial、HyperLogLog
数据类型简单描述使⽤场景
String string(字符串)是Redis最简单也是使⽤最⼴泛的数据结构,它的内部是⼀个字符数组。String(字符串)是动态字符串,允许修
改;它在结构上的实现类似于Java中的ArrayList(默认构造⼀个⼤⼩为10的初始数组),这是冗余分配内存的思想,也称为
预分配;这种思想可以减少扩容带来的性能消耗。当string(字符串)的⼤⼩达到扩容阈值时,将会对string(字符串)进⾏扩
容,string(字符串)的扩容主要有三种情况:1.长度⼩于1MB,扩容后为原先的两倍; length = length * 2 2.长度⼤于1MB,
扩容后增加1MB; length = length + 1MB 3. 字符串的长度最⼤值为 512MB
缓存、计
数器、分
布式锁
等。
List Redis的列表相当于Java语⾔中的LinkedList,它是⼀个双向链表数据结构(但是这个结构设计⽐较巧妙,后⾯会介绍),⽀
持前后顺序遍历。链表结构插⼊和删除操作快,时间复杂度O(1),查询慢,时间复杂度O(n)。Redis的list(列表)不是⼀个简
单。LinkedList,⽽是quicklist ——“快速列表”,quicklist是多个ziplist(压缩列表)组成的双向列表;
链表、异
步队列、
微博关注
⼈时间轴
列表……
Redis的hash(字典)相当于Java语⾔中的HashMap,它是根据散列值分布的⽆序字典,内部的元素是通过键值对的⽅式存
储。hash(字典)的实现与Java中的HashMap(JDK1.7)的结构也是⼀致的,它的数据结构也是数组+链表组成的⼆维结构,
节点元素散列在数组上,如果发⽣hash碰撞则使⽤链表串联在数组节点上。Redis中的hash(字典)存储的value只能是字符串
⽤户信
Hash值,此外扩容与Java中的HashMap也不同。Java中的HashMap在扩容的时候是⼀次性完成的,⽽Redis考虑到其核⼼存取是单线程的性能问题,为了追求⾼性能,因⽽采取了渐进式rehash策略。渐进式rehash指的是并⾮⼀次性完成,它是多次完成
的,因此需要保留旧的hash结构,所以Redis中的hash(字典)会存在新旧两个hash结构,在rehash结束后也就是旧hash的值
全部搬迁到新hash之后,新的hash在功能上才会完全替代以前的hash。息、Hash 表……
Set Redis的set(集合)相当于Java语⾔⾥的HashSet,它内部的键值对是⽆序的、唯⼀的。它的内部实现了⼀个所有value为null
的特殊字典。集合中的最后⼀个元素被移除之后,数据结构被⾃动删除,内存被回收。
去重功
能、赞、
踩、共同
好友……
ZSet zset(有序集合)是Redis中最常问的数据结构。它类似于Java语⾔中的SortedSet和HashMap的结合体,它⼀⽅⾯通过set来
保证内部value值的唯⼀性,另⼀⽅⾯通过value的score(权重)来进⾏排序。这个排序的功能是通过Skip List(跳跃列表)
来实现的。zset(有序集合)的最后⼀个元素value被移除后,数据结构被⾃动删除,内存被回收。
粉丝列
表、学⽣
成绩排
序、访问
量排⾏
榜、点击
量排⾏
榜……
Bitmaps Bitmaps 称为位图,严格来说它不是⼀种数据类型。Bitmaps底层就是字符串(key-value)byte数组。我们可以使⽤普通的
get/set直接获取和设值位图的内容,也可以通过Redis提供的位图操作getbit/setbit等将byte数组看成“位数组”来处理。
Bitmaps 的“位数组”每个单元格只能存储0和1,数组的下标在Bitmaps中称为偏移量。Bitmaps设置时key不存在会⾃动⽣
成⼀个新的字符串,如果设置的偏移量超出了现有内容的范围,就会⾃动将位数组进⾏零扩充
员⼯打
卡……
Geospatial Geospatial是Redis在3.2版本以后增加的地理位置GEO模块附近的⼈,在线点
餐“附近的餐馆”……
HyperLogLog HyperLogLog是⽤来做基数统计的算法,它提供不精确的去重计数⽅案(这个不精确并不是⾮常不精确),标准误差是
0.81%,对于UV这种统计来说这样的误差范围是被允许的。HyperLogLog的优点在于,输⼊元素的数量或者体积⾮常⼤时,
基数计算的存储空间是固定的。在Redis中,每个HyperLogLog键只需要花费12KB内存,就可以计算接近2^64个不同的基
数。但是HyperLogLog只能统计基数的⼤⼩(也就是数据集的⼤⼩,集合的个数),他不能存储元素的本⾝,不能向set集合
那样存储元素本⾝,也就是说⽆法返回元素。
基数统计
⽐如UV
数据类型简单描述
使⽤场
这个我的理解⼤致是这样的⾯试官!!
Redis的数据结构均可以通过EXPIRE key seconds 的⽅式设置key的过期时间(TTL)。我们也习惯的认为Redis的key过期时间到了,就会⾃动删除,显然这种想法并不正确。Redis的设计考虑到性能/内存等综合因素,设计了⼀套过期策略。
主动删除(惰性删除)
被动删除(定期策略)
主动删除(惰性删除)指的是当key被访问的时候,先校验key是否过期,如果过期了则主动删除。 被动删除(定期策略)指的是Redis服务器定时随机的测试key的过期时间,如果过期了则被动删除。被动删除的存在必不可少,因为存在⼀些过期且永久不在访问的key,如果都依赖主动删除,那么它们将会永久占⽤内存。 Redis为了保证提供⾼性能服务,被动删除过期的key,采⽤了贪⼼策略/概率算法,默认每隔10秒扫描⼀次,具体策略如下:
1. 从过期字典(设置了过期时间的key的集合)中随机选择20个key,检查其是否过期
2. 删除其中已经过期的key
3. 如果删除的过期key数量⼤于25%,则重复步骤1
此外开发在设计Redis缓存架构时,⼀定要注意要尽可能的避免(禁⽌)将⼤量的key设置为同⼀过期时间,因为结合被动删除可知,Redis 被动删除过期key时,会导致服务短暂的不可⽤;如果存在⼤量key同时过期,这会导致被动删除key的三个步骤循环多次,从⽽导致Redis 服务出现卡顿情况,这种情况在⼤型流量项⽬是⽆法接收的。 因此为了避免这种情况出现,⼀定要将⼀些允许过期时间不需要⾮常精确的key,设置较为随机的过期时间,这样就可以将卡顿时间缩⼩。
这个我的理解⼤致是这样的⾯试官!!
在分布式场景中我们常见的分布式锁解决⽅案有(如果⾃⼰都会可以把其他两种也在这带出来,如果不会那就别把⾃⼰坑了呀!):
1. 基于数据库锁机制实现的分布式锁
2. 基于Zookeeper实现的分布式锁
redis支持的数据结构
3. 基于Redis实现的分布式锁
⽽关于Redis实现分布式锁的⽅案是这样的。 如果Redis是在单机环境中我们可以通过,Redis提供的原⼦指令来实现分布式锁set key value [EX seconds] [PX milliseconds] [NX|XX]
为了防⽌A加的锁,被B删除了,可以加锁时传⼊客户端加锁标记,只有当客户端传⼊的标记和锁标记相同时才允许解锁,不过Redis并未提供这样的功能,我们只能通过Lua脚本来处理,因为Lua脚本可以保证多个指令的原⼦性执⾏。最后我们还要考虑锁的超时问题,如果客户端⼀直不释放锁肯定也是不⾏的,因此锁只能保证在指定的超时时间范围内不被其他客户端解锁,超时之后就⾃动释放了,这种情况很难我们可以这样优化:
1. 尽可能不要在Redis分布式锁中执⾏较长的任务,尽可能的缩⼩锁区间内执⾏代码,就像单JVM锁中的synchronized优化⼀样,我们
可以考虑优化锁的区间
2. 多做压⼒测试和线上真实场景的模拟测试,估算⼀个合适的锁超时时间
3. 做好Redis分布式锁超时任务未执⾏完的问题发⽣后,数据恢复⼿段的准备
如果是在分布式环境中,会增加⼀个新的问题,⽐如sentinel+⼀主多从环境中,可能存在客户端在主节点上申请了锁,但是同步未完成,主节点宕机了,此时新选举的主节点上锁是失效的。 对于这种情况的处理应该是这么考虑的,⾸先Redis主从同步直接⽆论如何都⽆法解决数据会有丢失的情况。所以我们考虑把像⼀个Redis申请锁,变成像多个单机Redis申请锁,只有⼤部分申请成功就⾏。这种思想就是RedLock(红锁)。 RedLock通过使⽤多个Redis实例,各个实例之间没有主从关系,相互独⽴;加锁的时候,客户端向所有的节点发送加锁指令,如果过半的节点set成功,就加锁成功。释放锁时,需要向所有的节点发送del指令来释放锁。 红锁虽然解决了主从同步的问题,但是带来新的复杂问题:
第⼀个问题是时钟漂移
第⼆个问题是客户端像不同的Redis服务端申请锁成功的时间是不同的
因此在RedLock中需要计算申请的锁的最⼩有效时长。假设客户端申请锁成功,第⼀个key设置成功的时间为TF,最后⼀个key设置成功的时间为TL,锁的超时时间为TTL,不同进程之间的时钟差异为CLOCK_DIFF,则锁的最⼩有效时长是:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。