Redis面试题

1.Redis持久化机制
Redis是一个支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。当Redis重启后通过将硬盘文件重新加载到内存,就能达到恢复数据的目的。
实现:单独创建fork()一个子进程,将当前父进程的数据库数据复制到子进程的内存中,然后由子进程写入到临时文件中,持久化过程结束了,再用这个临时文件替换上次的快照文件,然后子进程退出,释放内存。
两种持久化方式:
1)RDB是Redis默认的持久化方式,按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。
2)AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启会将重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
当两种方式同时开启时,数据恢复会优先选择AOF恢复。

2.缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级
缓存雪崩:原有缓存失效,新缓存未到期间。例如设置缓存时采用相同的过期时间,在同一时刻出现大面积的缓存过期,原本该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成了巨大的压力,严重的会造成数据库宕机,从而形成一系列连锁反应,造成整个系统崩溃。
解决方案:加锁或者队列的方式来保证不会有大量的线程对数据库一次性进行读写。还有一个简单的方法是将缓存失效时间分散开。

缓存穿透:缓存穿透是指用户在查询数据时,数据库中没有,自然缓存中也没有,这样导致用户在查询时先去缓存中查找一次未找到,然后再去数据库中查找,相当于进行了两次无用的查询。这样请求就绕过缓存直接查找数据库,这也是经常提到的缓存命中率问题。
解决办法: 常见的方案采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据一定会被这个bitmap拦截,从而避免了对底层存储系统查询的压力。还有一种简单的方法为如果查询返回的数据为空(不管这个数据是不存在还是系统故障),我们仍然把结果放入缓存中,但它的过期时间会很短,最长不超过五分钟。通过这个设置放结果放入缓存中,这样第二次从缓存中取值就有值了,不用再去访问数据库。

Bitmap:典型的就是哈希表。缺点为Bitmap对于每个元素只能记录1bit信息,如果还想完成额外的功能只能牺牲更多的空间和时间来完成了。

布隆过滤器:引入K个相互独立的哈希函数,保证在给定空间、误判率下,完成元素判重的过程。它的优点是空间效率和查询时间都远远超过一般的算法,缺点就是有一定的误识别率和删除困难。Bloom-Filter的核心思想是利用不同的Hash函数来解决冲突,用同一个Hash得到的两个URL的值可能相同。为了减少冲突,我们可以引入多个Hash,如果通过其中的一个Hash值得出某元素不在集合中,那么该元素肯定不在集合中。只有在所以Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。
缓存击穿:是指一个key非常热点,在不停扛着大并发,大并发集中对这个一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接在数据库请求数据。
解决方案:在访问key之前,采用set if not exits来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。

缓存预热:在系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,用户直接可以查询事先被预热的缓存数据。

缓存更新:1.定时去清理过期缓存。2.当用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
第一种方式的缺点是维护大量的key比较麻烦;第二种方式的缺点是每次用户请求过来都要去判断缓存是否失效,逻辑设计比较复杂。

缓存降级:当访问量剧增,响应时间慢或者不响应或非核心服务影响到核心流程性能时,扔要保证服务是可用的,即使有损服务,系统可以根据一些关键的数据进行降级,或者配置开关实现人工降级。降级的最终目的是保证核心服务可用。即使是有损的。
以参考日志级别设置预案:
1.一般:比如有些服务因为网络抖动或正在上线而超时,可以设置自动降级。
2.警告:一些服务在一段时间内成功率有波动(如95%到100%之间),可以自动降级或人工降级并发送警告。
3.错误:比如可用率降到90%以下,或数据库连接池被打爆,或访问量突然增加到系统能承受的最大阈值,此时可以设置为自动降级或人工降级。
4.严重错误:因为特殊原因,数据错误了,此时需要紧急人工降级。
服务降级的目的是为了防止Redis服务故障,引发数据库一系列雪崩问题,因此对于一些不重要的缓存数据,可以采取服务降级策略。例如比较常见的做法是当Redis出现问题时,不去访问数据库而是直接返回默认值给用户。

3.热点数据和冷数据
热点数据——缓存才有价值,
冷数据——大部分数据可能还没再次访问到就被挤出内存,不仅占用内存,而且价值不大。频繁修改数据,看情况考虑使用缓存。
对于热点数据,例如导航信息,缓存以后可能被读取上百万次。数据更新前至少读取两次,这样缓存才有意义,这是最基本的策略,如果缓存还没起作用就失效了,这样就没有太大的价值。
那存不存在修改频率很高,但又不得不考虑缓存的场景呢?比如这个读取接口对数据库的压力很大,但是又是热点数据,这时就要考虑通过缓存手段,减轻数据库压力,比如点赞数,收藏数,分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到Redis缓存,减少数据库压力。

4.Memecache与Redis的区别
1)存储方式:Memecache把数据全部存在内存中,断电之后会挂掉,数据不能超过内存大小。Redis有部分存在硬盘上,Redis可以持久化其数据
2)数据支持类型:Memecache所有值均是简单的字符串,Redis支持更为丰富的数据类型,提供list、set、hash等数据结构的存储。
3)使用底层模型不同:它们之间底层实现方式以及与客户端之间通信的应用协议不一样。
4)value的值大小不同:Redis最大可达512M;Memecache只有1M。
5)Redis支持数据备份,即master-slave模式的数据备份。

5.单线程的Redis为什么快?
1)纯内存操作
2)单线程操作,避免了频繁的上下文切换
3)采用了非阻塞I/O多路复用机制

6.Redis的数据类型以及每种数据类型的使用场景
1)String
最常规的get/set操作,value可以是String也可以是数字,一般做一些复杂的计数功能缓存。
2)hash
这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。
3)list
使用list的数据结构,可以做简单的消息队列功能,先进先出的原则。
4)set
因为set存放的是一堆不重复的集合,所以可以做全局去重的功能。为什么不用JVM自带的Set进行去重?因为系统一般是集群部署,使用JVM自带的Set需要再起一个公共服务,比较麻烦。
另外利用交集、并集、差集等操作可以计算共同的喜好,全部喜好,自己独有的喜好等功能。
5)sorted set
sorted set多了一个权重参数score,集合中的元素能够按照score进行排列,能够做排行榜应用。

7.Redis内部结构
1)dict本质上是为了解决算法中的查找问题,是一个用于维护key和value映射关系的数据结构。
2)sds等同于char*它可以存储任意二进制数据,不能像C语言字符串那样以字符‘\0’来标识字符串的结束,因此它必然有一个长度字段。
3)跳表是单层多指针的链表,查钊效率很高,堪比优化过的平衡二叉树,且比平衡数更易实现。
4)ziplist压缩表示一个编码后的列表,是由一系列特殊编码的连续内存块组成的顺序型数据结构。

8.Redis的过期策略以及内存淘汰机制
Redis采取的是定期删除+惰性删除策略。定期删除,Redis默认每个100ms检查是否有过期的key,如果有就删除,但并不是检查所有的key,而是随机抽取其中一部分进行检查。惰性删除是指在获取某个key的时候,会检查这个key的过期时间,如果过期则删除。但定期+惰性删除也有不足之处,如果定期删除时没抽中过期的key,而惰性删除也没生效,这样过期的key会越来越多,内存占用也会越来越高,所以还要采用内存淘汰机制。

9.Redis为什么是单线程的?
因为Redis是基于内存的操作,CPU不是Redis的瓶颈,机器内存的大小和网络带宽是最有可能的瓶颈。1)绝大部分操作是纯粹的内存操作,速度非常快。2)采用单线程避免了不必要的上下文切换和竞争条件。3)非阻塞I/O复用的优点:(1)速度快,因为数据在内存中,类似于Hashmap,查找等操作时间复杂度都是O(1)。(2)支持丰富的数据类型,如String,set,list,hash。(3)支持事务,操作都具有原子性,要么全部执行,要么全部不执行。(4)可用于缓存、消息,按key设置过期时间,过期后会自动删除。

10.有没有尝试过进行多机Redis的部署?如何保证数据的一致性?
主从复制,读写分离
一类是主数据库(master),一类是从数据库(slave),主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般只是读,并接收主数据库同步过来的数据。一个主数据库可以有多个从数据库,而一个从数据库只有一个主数据库。

11.对于大量请求怎么处理?
Redis是单线程程序,同一时刻只能处理一个客户端请求;Redis是通过IO多路复用来处理多个客户端请求。

12.Redis常见性能问题和解决方案
(1)Master最好不要做任何持久化工作,如RDB内存快照和AOF日志
(2)如果数据重要,可以用某个Slave开启AOF备份数据,设置为每秒同步一次
(3)为了主从复制的速度和连接的稳定性,Master和Slave最好的同一个局域网内
(4)尽量避免在压力很大的主库上增加从库
(5)主从复制用单向链表结构更稳定,即Master<—Slave1<—Slave2<—Slave3

13.讲解一下Redis线程模型
文件事件处理器分别包括套接字、I/O多路复用程序、文件事件分派器、事件处理器。使用I/O多路复用程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来负责处理这些事件。
I/O多路复用程序负责监听多个套接字,并向文件事件分派器传送那些产生了事件的套接字。
工作原理:I/O多路复用程序负责监听多个套接字,并向事件分派器传送那些产生了事件的套接字。尽管多个文件事件可能会并发出现,但I/O多路复用程序总是会将所有产生事件的套接字都入队到一个队列里面,然后通过这个队列,以有序(sequentially)、同步(synchronously)、每次一个套接字的方式向文件事件分派器传送套接字:当上一个套接字产生的事件被处理完毕后(该套接字为事件所关联的事件处理器执行完毕),I/O多路复用程序才会向文件事件分派器传送下一个套接字。如果一个套接字可读可写的话,那么服务器先读套接字再写套接字。在这里插入图片描述14.为什么Redis的操作是原子性的?怎么保证原子性?
原子性是指做一个操作不可再分,操作要么全部执行,要么不执行。
Redis操作之所以是原子性的,是因为Redis是单线程的。
Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。

15.Redis事务
Redis会将一个事务中的所有命令序列化,然后按顺序执行。
1)Redis不支持回滚,在事务失败时不进行回滚,而是继续执行余下的命令,所以Redis内部可以保持简单且快速。
2)如果在一个事务中的命令出现错误,那么所有命令都不会执行。
3)如果在一个事务中出现运行错误,那么正确的命令会被执行。
注:Redis中的Discard只是结束本次事务,但正确命令造成的影响任然存在。
Multi命令:用于开启一个事务,它总是但会OK。Multi命令执行后,客户端可以向服务器发生任意多条命令,但这些命令不会立即执行,而是被放到一个队列中,当Exec命令被调用时,所有队列中的命令才会被执行。
Exec命令:执行事务块内的所有命令。按命令的先后顺序执行,并返回事务块内所有命令的返回值。当操作被打断时,返回空值null。
Discard命令:客户端可以清空事务队列,并放弃执行事务,从事务状态中退出。
Watch命令:可以监控一个或多个键,一旦其中一个键被修改或删除,时候的事务就不会再执行,监控一直持续到Exec命令。

16.Redis实现分布式锁
Redis为单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系。
Redis中可以使用SETNX命令实现分布式锁。
解锁:使用Del key,命令就能释放锁。
解决死锁:1)通过Redis中的expire给锁设定最大持有时间,如果超过,则Redis帮我们释放锁。
2)使用SETNX key“当前系统时间+锁持有时间”和getset key“当前系统时间+锁持有时间”组合命令就可以实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值