目录
一、项目中哪些地方使用了redis
问题一:发生了缓存穿透该怎么解决?
方案一:缓存空数据
方案二:布隆过滤器
模拟面试
问题二: 发生了缓存击穿该怎么解决?
方案一:互斥锁
方案二:逻辑过期
模拟面试
问题三: 发生了缓存雪崩该怎么解决?
问题四: redis作为缓存,mysql的数据如何redis进行同步?(双写一致性)
这个时候我们就要考虑是先删除缓存,还是先修改数据库?
情况一:先删除缓存,再操作数据库
情况二:先操作数据库,再删除缓存
那么为什么要删除两次缓存?
为什么要延时删除?
业务场景一:那怎么保证双写一致呐?(一致性要求高的业务需求)
业务场景二:延迟一致怎么做?(允许短暂的不一致,实际开发中最为主流的)
方案一:基于MQ的异步通知
方案二:基于Canal的异步通知
模拟面试
问题五:redis作为缓存,数据的持久化是怎么做的?
RDB
AOF
RDB与AOD对比
模拟面试
编辑 问题六:假设redis的key过期之后,会立即删除吗?(数据过期策略)
惰性删除
定期删除
模拟面试
问题七: 假如缓存过多,内存是有限的,内存被占满了怎么办?(数据淘汰策略)
关于数据淘汰策略其他的面试问题?
1、数据库有1000万数据,Redis只能缓存20w数据,如何保证Redis中的数据都是热点数据?
2、Redis的内存用完了会发生什么?
模拟面试
问题八:redis分布式锁,是如何实现的?
情景模拟———抢券执行
redis分布式锁
Redis实现分布式锁如何合理的控制锁的有效时长?
redisson实现的分布式锁——执行流程
redisoon实现的分布式锁,是否可以重入呐?
redisson实现的分布式锁——主从一致性
模拟面试
问题九:redis集群有哪些方案?
主从复制
主从全量同步
主从增量同步 (slave重启或后期数据变化)
模拟面试
哨兵的作用
结构
作用
服务状态监控
Redis集群(哨兵模式)脑裂
模拟面试
分片集群结构
数据读写
模拟面试
总结
问题十:Redis是单线程的,但是为什么还那么快?
能解释一下I/O多路复用 模型?
用户空间和内核空间
阻塞IO
非阻塞IO
IO多路复用
Redis网络模型
模拟面试
以下问题包括知识点和面试题,看的时候直接看《模拟面试即可》,后续持续更新中。。。
一、项目中哪些地方使用了redis
面试官:我看你做的项目中,都用到了redis,你在最近的项目中哪些场景使用了redis?
一般这个问题要结合自己的实际项目经历回答,面试想这样问:一方面验证你的项目场景的真实性,二是为了作为深入发问的切入点
比如你回答了使用了:(可能会被问道的相关问题或知识点)
缓存:缓存三兄弟(穿透、击穿、雪崩)、双写一致、持久化、数据过期策略、数据淘汰策略
分布式锁:setnx、redisson
消息队列、延迟队列:何种数据类型
问题一:发生了缓存穿透该怎么解决?
以下是正常使用缓存的流程:
那么什么是缓存穿透呐?
查询一个不存在的数据,mysql查询不到数据也不会直接写入缓存,就会导致每次请求都去查询数据库
那为什么会出现这种情况呐?
通常呐,就是有人恶意攻击你的系统,有人知道你的请求路径,知道你的请求参数跟在请求路径后面的,就会制造一些假的id发起请求等等,就会冲击你的数据库,你的数据库的并发是不高的,请求到了一定的量就会击垮数据库。
解决方案:
方案一:缓存空数据
解决方案一:缓存空数据,查询返回的数据为空,仍把这个空数据进行缓存
优点:操作简单
缺点:消耗内存,可能会发生不一致的问题
方案二:布隆过滤器
解决方案二:布隆过滤器
优点:内存占用较少,没有多余key缺点:实现复杂,存在误判
布隆过滤器:
bitmap(位图):相当于一个以(bit)位为单位的数组,数组中每个单元只能存储二进制数0或1
布隆过滤器作用:布隆过滤器可以用于检索一个元素是否再一个集合中。可能产生误判:
误判率:数组越小误判率就越大,数组越大误判率就越小,但是同时带来了更多的内存消耗
模拟面试
问题二: 发生了缓存击穿该怎么解决?
什么是缓存击穿?
给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把数据库压垮
解决方案:
方案一:互斥锁
特点:强一致,性能差
方案二:逻辑过期
特点:高可用、性能优、不能保证数据的绝对一致
模拟面试
问题三: 发生了缓存雪崩该怎么解决?
什么是缓存雪崩?
在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力
解决方案:
- 给不同的key的TTL添加随机值
- 利用Redis集群提高服务的可用性
- 给缓存业务添加降级限流策略 (降级可作为系统的保底策略,适用于穿透、击穿、雪崩)
- 给业务添加多级缓存
模拟面试:
问题四: redis作为缓存,mysql的数据如何redis进行同步?(双写一致性)
一定要设置前提,,,结合自己的项目业务背景去讲!!!!
看看到底属于哪一种:是一致性要求高的业务需求,还是说是允许延迟一致?
双写一致性:当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致
读操作:缓存命中,直接返回;缓存未命中查询数据库,写入缓存,设定超时时间
写操作:延迟双删
这个时候我们就要考虑是先删除缓存,还是先修改数据库?
情况一:先删除缓存,再操作数据库
正常情况:
不正常情况:因为线程是交替执行的,这种方式可能会出现脏数据的现象
情况二:先操作数据库,再删除缓存
正常情况:
不正常情况:这种方式可能会出现脏数据的现象
那么为什么要删除两次缓存?
降低脏数据的出现
为什么要延时删除?
业务场景一:那怎么保证双写一致呐?(一致性要求高的业务需求)
但是这样的话,性能就有点差了
当然我们都是知道的,放入redis的数据一般都是读多写少
所以使用读写锁就可以了
特点:强一致,性能差(强一致性情况下才会使用)
共享锁:读锁readLock,加锁之后,其他线程可以共享读操作
排他锁:独占锁writeLock,加锁之后,阻塞其他线程读写操作
业务场景二:延迟一致怎么做?(允许短暂的不一致,实际开发中最为主流的)
方案一:基于MQ的异步通知
方案二:基于Canal的异步通知
模拟面试
强一致性回答:
最终一致性回答:
问题五:redis作为缓存,数据的持久化是怎么做的?
RDB
什么是RDB呐?
RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中所有数据记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,回复数据
人工主动备份:
Redis自动备份:
Redis内部有触发RDB的机制,可以再redis.conf文件中找到,格式如下:
RDB的执行原理:
bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入RDB文件。
fork采用的是copy-write技术:
- 当主进程执行读操作时,访问共享内存;
- 当主进程执行写操作时,则会拷贝一份数据,执行写操作。
优点 :
1. 整个Redis数据库将只包含⼀个⽂件 dump.rdb,⽅便持久化。2. 容灾性好,⽅便备份。3. 性能最⼤化,fork ⼦进程来完成写操作,让主进程继续处理命令,所以是 IO 最⼤化。使⽤单独⼦进程来进⾏持久化,主进程不会进⾏任何 IO 操作,保证了 redis 的⾼性能4. 相对于数据集⼤时,⽐ AOF 的启动效率更⾼。
缺点:1. 数据安全性低。RDB 是间隔⼀段时间进⾏持久化,如果持久化之间 redis 发⽣故障,会发⽣数据丢失。所以这种⽅式更适合数据要求不严谨的时候)2. 由于RDB是通过fork⼦进程来协助完成数据持久化⼯作的,因此,如果当数据集较⼤时,可能会导致整个服务器停⽌服务⼏百毫秒,甚⾄是1秒钟
AOF
什么是AOF呐?
AOF全称Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看作是命令日志文件。
优点:
1. 数据安全,Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是⾮常⾼的,所差的是⼀旦系统出现宕机现象,那么这⼀秒钟之内修改的数据将会丢失。⽽每修改同步,我们可以将其视为同步持久化,即每次发⽣的数据变化都会被⽴即记录到磁盘中。。2. 通过 append 模式写⽂件,即使中途服务器宕机也不会破坏已经存在的内容,可以通过 redischeck-aof ⼯具解决数据⼀致性问题。3. AOF 机制的 rewrite 模式。定期对AOF⽂件进⾏重写,以达到压缩的⽬的
缺点:
1、 因为是记录命令,AOF文件会比RDB文件大得多。而且AOF会记录对同一个key的多次写操作,单只有最后一次写操作才有意义。通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,用最少的命令达到相同的效果。
2、 数据集⼤的时候,⽐ rdb 启动效率低。3、运⾏效率没有RDB⾼
Redis也会在触发阈值时自动去重写AOF文件。阈值也可以在redis.conf中配置:
RDB与AOD对比
模拟面试
问题六:假设redis的key过期之后,会立即删除吗?(数据过期策略)
什么是数据过期策略呐?
Redis对数据设置数据的有效时间,数据过期以后,就需要将数据从内存中删除掉。可以按照不同的规则进行删除,这种删除规则就被称之为数据的删除策略(数据过期策略)
惰性删除
惰性删除:设置该key过期时间后,我们不去管它,当需要该key时,我们再检查其是否过期,如果过期,我们就删掉它,反之返回该key
优点:对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查
缺点:堆内存不好,如果一个key已经过期,但是一直没有使用,那么该key就会一直存在内存中,内存永远不会释放
定期删除
什么是定期删除呐?
每隔一段时间,我们就对一些key进行检查,删除里面过期的key(从一定数量的数据库中取出一定数量的随机key进行检查,并删除其中的过期key)。
定期清理的两种模式
- SLOW模式 是定时任务,执行频率默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf的hz选项来调整这个次数
- FAST模式执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms
优点: 可以通过限制删除操作执行的时长和频率来减少删除操作对CPU的影响。另外定期删除,也能有效释放过期键占用的内存。
缺点:难以确定删除操作执行的时长和频率。
Redis的过期删除策略:惰性删除 + 定期删除两种策略进行配合使用
模拟面试
问题七: 假如缓存过多,内存是有限的,内存被占满了怎么办?(数据淘汰策略)
什么是数据淘汰策略?
当Redis中的内存不够用时,此时再向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除掉,这种数据的删除规则被称之为内存的淘汰策略。
LRU例子:key1是在3s之前访问的,key2是在9s之前访问的,删除的就是key2
LFU例子:key1最近5s访问了4次,key2最近5s访问了9次,删除的就是key1
关于数据淘汰策略其他的面试问题?
1、数据库有1000万数据,Redis只能缓存20w数据,如何保证Redis中的数据都是热点数据?
2、Redis的内存用完了会发生什么?
模拟面试
以上问题都是基于你的项目:redis使用的场景是缓存这部分;下面则是基于你的项目redis使用场景是分布式锁
问题八:redis分布式锁,是如何实现的?
情景模拟———抢券执行
正常情况:
异常情况:
当线程1执行完,此时的库存是0,当线程2执行完之后,库存再减一,就变成-1了,就会出现超卖的情况
加锁操作:
适用于单体项目,服务只部署在一台服务器上的,这个方法是没问题的,但是往往我们的服务都是集群的
分布式锁解决这个问题:
、
redis分布式锁
Redis实现分布式锁主要利用Redis的setnx命令。setnx是SET if not exists(如果不存在,则SET)的缩写。
1. ⾸先利⽤setnx来保证:如果key不存在才能获取到锁,如果key存在,则获取不到锁2. 然后还要利⽤lua脚本来保证多个redis操作的原⼦性3. 同时还要考虑到锁过期,所以需要额外的⼀个看⻔狗定时任务来监听锁是否需要续约4. 同时还要考虑到redis节点挂掉后的情况,所以需要采⽤红锁的⽅式来同时向N/2+1个节点申请锁,都申请到了才证明获取锁成功,这样就算其中某个redis节点挂掉了,锁也不能被其他客户端获取到
Redis实现分布式锁如何合理的控制锁的有效时长?
根据业务执行时间预估
给锁续期
redisson实现的分布式锁——执行流程
实例代码:
redisoon实现的分布式锁,是否可以重入呐?
可重入,利用hash结构记录线程id和重入次数
redisson实现的分布式锁——主从一致性
RedLock(红锁):不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁(n/2+1),避免在一个redis实例上加锁。
模拟面试
下面则是redis其他的问题
问题九:redis集群有哪些方案?
主从复制
哨兵模式
分片集群
主从复制
单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。
通过执⾏slaveof命令或设置slaveof选项,让⼀个服务器去复制另⼀个服务器的数据。主数据库可以进 ⾏读写操作,当写操作导致数据变化时会⾃动将数据同步给从数据库。⽽从数据库⼀般是只读的,并接 受主数据库同步过来的数据。⼀个主数据库可以拥有多个从数据库,⽽⼀个从数据库只能拥有⼀个主数据库。
主从全量同步
1. 主节点通过bgsave命令fork⼦进程进⾏RDB持久化,该过程是⾮常消耗CPU、内存(⻚表复制)、硬盘IO的2. 主节点通过⽹络将RDB⽂件发送给从节点,对主从节点的带宽都会带来很⼤的消耗3. 从节点清空⽼数据、载⼊新RDB⽂件的过程是阻塞的,⽆法响应客户端的命令;如果从节点执⾏bgrewriteaof,也会带来额外的消耗
主从增量同步 (slave重启或后期数据变化)
1. 复制偏移量:执⾏复制的双⽅,主从节点,分别会维护⼀个复制偏移量offset2. 复制积压缓冲区:主节点内部维护了⼀个固定⻓度的、先进先出(FIFO)队列 作为复制积压缓冲区,当主从节点offset的差距过⼤超过缓冲区⻓度时,将⽆法执⾏部分复制,只能执⾏全量复制。3. 服务器运⾏ID(runid):每个Redis节点,都有其运⾏ID,运⾏ID由节点在启动时⾃动⽣成,主节点会将⾃⼰的运⾏ID发送给从节点,从节点会将主节点的运⾏ID存起来。 从节点Redis断开重连的时候,就是根据运⾏ID来判断同步的进度:○ 如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使⽤部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况);○ 如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进⾏全量复制。
模拟面试
哨兵的作用
Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复
结构
作用
服务状态监控
Redis集群(哨兵模式)脑裂
模拟面试
分片集群结构
数据读写
Redis分片集群引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽
模拟面试
总结
Redis提供了三种集群策略:1. 主从模式:这种模式⽐较简单,主库可以读写,并且会和从库进⾏数据同步,这种模式下,客户端 直接连主库或某个从库,但是但主库或从库宕机后,客户端需要⼿动修改IP,另外,这种模式也⽐ 较难进⾏扩容,整个集群所能存储的数据受到某台机器的内存容量,所以不可能⽀持特⼤数据量2. 哨兵模式:这种模式在主从的基础上新增了哨兵节点,但主库节点宕机后,哨兵会发现主库节点宕 机,然后在从库中选择⼀个库作为进的主库,另外哨兵也可以做集群,从⽽可以保证但某⼀个哨兵 节点宕机后,还有其他哨兵节点可以继续⼯作,这种模式可以⽐较好的保证Redis集群的⾼可⽤,但 是仍然不能很好的解决Redis的容量上限问题。3. Cluster模式:Cluster模式是⽤得⽐较多的模式,它⽀持多主多从,这种模式会按照key进⾏槽位的 分配,可以使得不同的key分散到不同的主节点上,利⽤这种模式可以使得整个集群⽀持更⼤的数据容量,同时每个主节点可以拥有⾃⼰的多个从节点,如果该主节点宕机,会从它的从节点中选举⼀ 个新的主节点。对于这三种模式,如果Redis要存的数据量不⼤,可以选择哨兵模式,如果Redis要存的数据量⼤,并且 需要持续的扩容,那么选择Cluster模式。
问题十:Redis是单线程的,但是为什么还那么快?
能解释一下I/O多路复用 模型?
用户空间和内核空间
阻塞IO
非阻塞IO
IO多路复用
IO多路复用:是利用单个线程来同时监听多个Socket,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。不过监听Scoket的方式、通知的方式又有多种实现,常见的有:
select
poll
epoll
Redis网络模型
Redis通过IO多路复用来提高网络性能,并支持各种不同的多路复用实现,并且将这些实现进行封装,提供了统一的高性能事件库