Redis面试题

1.Redis的使用场景:

        ①缓存:穿透、击穿、雪崩问题;双写一致、持久化问题;数据过期、淘汰策略问题。

        ②分布式锁:setnx、redisson。

        ③计数器

        ④保存token

        ⑤消息队列

        ⑥延迟队列

2.Redis缓存穿透如何解决?

        缓存穿透:查询一个不存在的数据,DB查询不到数据也不会写入Redis中,导致每次请求都会查询数据库,数据库压力过大会宕机(通过假的id恶意查询)。

        解决方案

        ①缓存空数据(查询结果为空的数据,仍把空结果进行缓存{key:1,value:null});

        缺点:内存消耗,存在数据不一致的问题(查询后数据更新,再次查询缓存中仍为null)。

        ②采用布隆过滤器(拦截不存在的数据,即请求先通过布隆过滤器,若存在则进入缓存查询,否则直接返回。注意:缓存预热时需要预热布隆过滤器);

        布隆过滤器:一个存放二进制0或1的数组,通过对key三次hash取得对应数组下标并将其初始化的二进制0改为1。查询时三次hash后的数组下标内的值都为1则存在,否则不存在。

        缺点:实现复杂,存在误判(查询数组下标内存放的值正好都为1,可通过设置误判率来解决或增大数组长度)。

3.缓存击穿问题是什么?如何解决缓存击穿?

        缓存击穿:设置为过期时间的key过期时,恰好该时间对这个key有大量的并发请求,请求需从后端DB加载数据并回设到缓存中,大量并发的请求可能会瞬间压垮数据库

        解决方案

        ①添加互斥锁(具有强一致性,性能差):只能有一个线程获取锁并重建数据,在该线程写入缓存之前,其他线程需循环等待。

        ②逻辑过期(具有高可用性,性能优):获取到互斥锁的访问线程会重新开启一个线程重新构建数据库,开启后该线程以及在数据存入缓存之前访问的线程均返回过期数据,数据存入缓存后访问的线程获取新数据。

4.什么是缓存雪崩?如果发生缓存雪崩该如何解决?

        缓存雪崩:是指在同一时段大量的缓存key同时失效或Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

        解决方案

        ①给不同的key设置不同的过期时间(如:对key的TTL添加随机值,已降低过期重复率);

        ②对于Redis服务宕机问题,可以利用Redis集群提高服务的可用性(如:哨兵模式、集群模式);

        ③给缓存业务添加降级限流策略(如:在ngxin或spring cloud gateway中设置限流规则),该策略可以作为系统的保底策略,适用于穿透、击穿、雪崩

        ④给业务添加多级缓存(如:添加Guava或Caffeine作为一级缓存、Redis作为二级缓存)。

5.MySQL的数据如何与Redis进行同步呢(即双写一致性)?

        先介绍业务背景:是对一致性要求高?还是允许延迟一致?

        (1)双写一致性(即实时性较高):当修改了数据库的数据同时也需要更新缓存中的数据,要使缓存和数据库中的数据保持一致。

        读数据:缓存命中,直接返回;缓存未命中,查询数据库写入缓存,设定超时时间。

        写数据:延迟双删(即删除缓存----修改数据库--(延时)--删除缓存),该方法不可用存在一下问题。

                ●先删除缓存还是先修改数据库(都可能存在脏数据的问题)

                ●为什么要删除两次缓存?(存在脏数据的风险,所以删除两次,但仅能控制一部分)

                ●为什么要延时删除?(采用中从结构,主节点更新后,从节点需要一定的时间更新,而延时多久不好控制,延时过程中肯能存在脏数据)

        因此一般不采用延迟双删的方法,而是过添加分布式锁(排他锁、共享锁)来解决读脏数据的问题,该方法保证了数据的强一致性,但性能较差。

        (2)允许数据短暂的不一致(即实时性不是很高,可采用异步的方式)

                ①异步通知保证数据的最终一致性

                ②基于Canal的异步通知

6.Redis作为缓存,数据的持久化是什么做的?

        ①RDB(Redis数据快照)----即将内存中的所有数据都记录到磁盘中,当Redis实例故障重启后,从磁盘读取快照文件恢复数据。

        ②AOF(追加文件)----即处理每一个写命令时都会记录在AOF文件中,可以看作是命令日志文件。AOF默认是关闭的,需要修改redis.conf配置文件来开启AOF。

7.redis的key过期后,会立即删除吗?

        数据过期策略:当Redis中数据所设置的有效时间过期后,需要将数据从内存中删除。此时可以按照不同的规则(即数据的删除策略,例如:惰性删除、定期删除,注:一般采用两者配合使用)进行删除。

        ①惰性删除----设置该key过期后,不去管它,当需要key时再检查是否过期,如果过期就删掉该数据,否则返回该key。

        注意:对CPU友好,无需花费时间检查key是否过期;但是对内存不友好,如大量key过期且一直没有使用,则大量内存得不到释放。

        ②定期删除----每隔一段时间,就会对一些key进行检查,删除里面过期的key。存在SLOW模式和FAST模式。

        注意:可通过限制删除操作执行的时长和频率来减少删除操作对cpu的影响,但难以确定删除操作执行的时长和频率。

8.假如缓存过多,内存是有限的,内存被占满了怎么办?

        数据的淘汰策略:当Redis中内存不够用时,此时在想向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除掉,这种数据的删除规则被称之为内存的淘汰策略。

        Redis中支持的8种不同策略:

        ①noeviction:不淘汰任何key,内存满时不允许写入新数据,该策略为Redis的默认策略。

        ②volatile-ttl:对设置了TTL的key,比较key的剩余TTL值,剩余的TTL值越小越先被淘汰。

        ③allkeys-random:对全体key,随即进行淘汰。

        ④volatile-random:对设置了TTL的key,随即进行淘汰。

        ⑤allkeys-lru:对全体key,基于LRU算法进行淘汰。

        ⑥volatile-lru:对设置了TTL的key,基于LRU算法进行淘汰。

        ⑦allkeys-lfu:对全体key,基于LFU算法进行淘汰。

        ⑧volatile-lfu:对设置了TTL的key,基于LFU算法进行淘汰。

        使用建议:

        ●优先使用allkeys-lru策略。充分利用LRU算法的优势,把最近最常访问的数据留在缓存中。如果业务有明显的冷热数据区分,建议使用。

        ●如果业务中数据访问频率差别不大,没有明显的冷热数据区分,建议使用allkeys-random,随机选择淘汰。

        ●如果业务中有置顶的需求,可以使用volatile-lru策略,同时置顶数据不设置过期时间,这些数据就一直不被删除,而是会淘汰其他设置过期时间的数据。

        ●如果业务中有短时高频访问的数据,可以使用allkeys-lfu或volatile-lfu策略。 

9.Redis作为分布式锁,是如何实现的?

        应用场景:秒杀抢购、定时任务、幂等性场景。

        注意:抢单任务可通过加锁解决,本地jvm下加synchronized锁可解决;如果在集群情况下,synchronized锁只能用于本地多线程情况,无法解决集群下的多线程冲突,这时需要加入外部锁(分布式锁)来解决冲突。

         采用redisson实现的分布式锁,其底层是setnx和lua脚本(保证原子性);redisson实现的分布式锁中的有效时长控制,通过WatchDog来监控线程任务是否执行完成,若没有则会给持有锁的线程续期(默认是每隔10秒续期一次)。

        redisson锁是可重入锁,多个锁重入需要判断是否是当前线程,在redis中进行存储的时候使用hash结构来存储线程信息重入次数

        redisson锁不能解决主从数据一致的问题,但可以提供红锁来解决,使用红锁性能将会降低。如果业务中非要保证数据的强一致性,建议采用zookeeper实现。

10.Redis集群有哪些方案?

        Redis的集群方案:主从复制、哨兵模式、分片集群。

        (1)主从复制(解决高并发问题):单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。一般都是一主多从,主节点负责写数据,从节点负责读数据。

        主从数据同步的流程:

                1.全量同步

                ①从节点请求主节点同步数据(replication id、offset);

                ②主节点判断是否是第一次请求,是第一次请求与从节点同步版本信息(replication id和offset);

                ③主节点执行bgsave生成RDB文件,并发送给从节点去执行;

                ④在RDB生成执行期间,主节点会以命令的方式记录到缓存区(一个日志文件);

                ⑤把生成之后的日志文件发送给从节点进行同步。

                2.增量同步

                ①从节点请求主节点同步数据,主节点判断是不是第一次请求,不是第一次就获取从节点的offset值;

                ②主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步。

        (2)哨兵模式(解决高可用问题):主从集群不能保证集群的高可用,当主节点宕机后集群丧失了写数据的能力,而Redis提供了哨兵(sentinel)机制来实现主从集群的自动故障恢复。作用如下:

                ①监控----Sentinel会不断检查master和slave是否按预期工作;

                ②自动故障恢复----如果master故障,sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主;

                ③通知----sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新消息推送给Redis的客户端。

        Redis集群的脑裂问题:由于网络原因,主节点master和哨兵处于不同的网络分区,哨兵检测不到主节点从而去检测从节点,从而导致哨兵将从节点选为新的主节点,从而导致出现两个主节点master。而客户端仍然链接的旧的master,当网络恢复后,哨兵会将旧的master强制降为slave并与新的master进行同步数据(同步数据时,旧的master会清空节点内数据),从而导致网络问题期间客户端写入旧的master中的数据丢失。

        解决:可以修改Redis的配置,可以设置最少的节点数量(min-replicas-to-write)以及缩短主从数据同步的延迟时间(min-replicas-max-log),达不到要求就拒绝请求,就可以避免大量数据丢失。

        (3)分片集群(解决海量数据存储和高并发写的问题)

           ◆集群中有多个master,每个master保存不同数据,且都可以有多个slave节点;

           ◆master之间通过ping监测彼此之间的状态;

           ◆客户端请求可以访问集群任意节点,最终都会被转发到正确的节点。

        其Redis分片集群中的数据存储和读取如下

           ●Redis分片集群引入哈希槽的感念,Redis集群有16384个哈希槽,并将16384个插槽分配到不同的实例;

           ●读写数据:根据key的有效部分(若key前面有大括号,则大括号内的内容就是有效部分;否则,key本身作为有效部分)计算哈希值,对16384取余,余数作为插槽位置,寻找插槽所在的实例。

11.Redis是单线程的,但是为什么还是那么快?

        (1)Redis是纯内存操作,所以执行速度非常的快;

        (2)采用单线程避免了不必要的上下文切换可竞争条件,多线程则还要考虑线程安全问题;

        (3)使用I/O多路复用模型,非阻塞IO。

12.解释一下I/O多路复用模型?

        Redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度,I/O多路复用模型主要就是实现了高效的网络请求。

        常见的IO模型:阻塞IO(Blocking IO)、非阻塞IO(Nonblocking IO)、IO多路复用(IO Multiplexing)。

        I/O多路复用:是指利用单个线程来监听多个Socket,并在某个Socket可读、可写时得到通知,从而避免无效等待,充分利用cpu资源。目前的I/O多路复用都是采用epoll模型实现的,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,从而提升性能。

        监听Socket的方式:select、poll、epoll。其中select和poll只会通知用户进程有Socket就绪,但不确定具体是哪个Socket,需要用户进程逐个遍历Socket来确定;而epoll则会在通知用户进程Socekt就绪的同时,把已就绪的Socket写入用户空间。

        Redis网络模型:就是使用I/O多路复用结合事件的处理器来应对多个Socket请求

                ▶连接应答处理器;

                ▶命令回复处理器,在Redis6.0之后,为了提升更好的性能,使用了多线程处理回复事件;

                ▶命令请求处理器,在Redis6.0之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值