redis使用场景:
1.redis缓存穿透问题:
什么是缓存穿透:
查询一个不存在的数据,mysql查询不到数据也不会直接写入缓存,就会导致每次请求都查数据库
解决方案:
方案一:
缓存空数据,查询返回的数据为空,仍把这个空结果进行缓存
优点:简单
缺点:消耗内存,可能会发生不一致的问题。
方案二:
布隆过滤器
优点:内存占用较少,没有多余的key
缺点:实现复杂,存在误判
bitmap(位图):相当于是以一个(bit)位为单位的数组,数组中每个单元只能存储二进制数0或1
布隆过滤器作用:布隆过滤器可以用于检索一个元素是否在一个集合中。
会产生误判(不能被消除):
误判率:数组越小误判率就越大,数组越大误判率就越小,但是同时带来了更多的内存消耗。
减小误判:
2.redis缓存击穿问题
什么是缓存击穿:
给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮。
解决方案一:互斥锁(分布式锁)(强一致性:性能比较差)
解决方案二:逻辑过期(高可用,性能优,不能保证数据绝对一致)
3.redis缓存雪崩问题
缓存雪崩:
是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。
4.redis缓存双写一致性问题
双写一致性:
当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致。
方案一:
先删除缓存还是先修改数据库?
都会出现问题(脏数据问题)
为什么要删除两次?
为什么要延时一会?
控制一部分脏数据问题,不能保证强一致性。
方案二(一致性要求高):
加互斥锁(强一致性,性能低)
读锁
写锁(排它锁):
方案二 (异步通知保证数据的最终一致性):
1.mq
2.canal
面试问题:
redis作为缓存,mysql的数据如何与redis进行同步呢?(双写一致性)
1.介绍自己简历上的业务,我们当时是把文章的热点数据存入到了缓存中,虽然是热点数据,但是实时要求性并没有那么高,所以,我们当时采用的是异步的方案同步的数据。
2.我们当时是把抢券的库存存入到了缓存中,这个需要实时的进行数据同步,为了保证数据的强一致性,我们当时采用的是redisson提供的读写锁来保证数据的同步。
那你来介绍一下异步的方案(你来介绍一下redisson读写锁的这种方案)
1.允许延时一致的业务,采用异步通知
使用mq中间中间件,更新数据之后,通知缓存删除
利用canal中间件,不需要修改业务代码,伪装为mysql的一个从节点,canal通过读取binlog数据更新缓存
2.强一致性,采用Redisson提供的读写锁
共享锁:读锁readLock,加锁之后,其他线程可以共享读操作
排他锁:独占锁writeLock也叫加锁之后阻塞其他线程读写操作
面试回答:
Redis持久化
RDB:
RDB全称Redis Database Backup life(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。
Redis内部有出发RDB的机制,可以在redis.conf文件中找到,格式如下:
RDB执行原理:
bgsave开始时会fork(克隆)主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入RDB文件。
AOF:
AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看作是命令日志文件。
因为是记录命令,AOF文件会比RDB文件大的多。而且AOF会记录对同一个key的多次写操作,但只有最后一次写操作才有意思。通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,用最少的命令达到相同效果。
面试问题:
Redis数据过期策略
Redis数据删除策略-惰性删除
惰性删除:设置该key过期时间后,我们不管它,当需要key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key。
优点:对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查
缺点:对内存不友好,如果一个key已经过期,但是一直没有使用,那么该key就会一直存在内存中,内存永远不会释放。
Redis数据删除策略-定期删除
定期删除:每隔一段时间,我们就对一些key进行检查,删除里面过期的key(从一定数量的数据库中取出一定数量的随机key进行检查,并删除其中过期key)
定期清理有两种模式:
SLOW模式是定时任务,执行频率默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf的hz选项来调整这个次数
FAST模式执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms
优点:可以通过限制删除操作执行的时长和频率来减少删除操作对CPU的影响。另外定期删除,也能有效释放过期键占用的内存。
缺点:难以确定删除操作执行的时长和频率。
Redis的过期删除策略:惰性删除 + 定期删除两种策略进行配合使用。
面试问题:
Redis数据淘汰策略
数据淘汰策略:当Redis中的内存不够用时,此时在向Redis中添加新的key,那么Redis就会按照某一规则将内存中的数据删除掉,这种数据的删除规则则被称之为内存的淘汰策略。
Redis'支持8种不同策略来选择要删除的key
数据淘汰策略-使用建议
1.优先使用allkeys-lru策略。充分利用LRU算法的优势,把最近最常访问的数据留在缓存中。如果业务有明显的冷热数据区分,建议使用。
2.如果业务中数据访问频率差别不大,没有明显冷热数据区分,建议使用allkeys-random,随机选择淘汰。
3.如果业务中有置顶的需求,可以使用volatile-lru策略,同时置顶数据不设置过期时间,这些数据就一直不被删除,会淘汰其他设置过期时间的数据。
4.如果业务中有短时高频访问的数据,可以使用allkeys-ifu或volatile-lfu策略
有关数据淘汰策略其他的面试问题
1.数据库中有1000万数据,Redis只能缓存20W数据,如何保证Redis中的数据都是热点数据?
使用allkeys-lru(挑选最近最少使用的数据淘汰)淘汰策略,留下来的都是经常访问的热点数据
2.Redis的内存用完了会发生什么?
主要看数据淘汰策略是什么?如果是默认的配置(noeviction),会直接报错。
总结:
1.Redis提供了8种不同的数据淘汰策略,默认是noeviction不删除任何数据,内存不足直接报错。
2.LRU:最近最少使用。用当前时间减去最后一次的访问时间,这个值越大则淘汰优先级越高。
3.LFU:最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高。
平时开发过程中用的比较多的就是allkeys-lru(结合自己的业务场景)
面试问题:
Redis分布式锁:
Redis实现分布式锁主要利用Redis的sentnx命令。setnx是SET if not exists(如果不存在,则SET)的简写
代码实现:
redisson实现的分布式锁-可重入
redisson实现的分布式锁-主从一致性
Redis分布式锁是如何实现的:
面试问题:
Redis其他面试问题:
主从复制
主从数据同步原理:
主从全量同步:
主从增量同步(slave(从节点)重启或后期数据变化):
面试问题:
Redis哨兵模式:
服务状态监控:
Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例都发送ping命令:
主观下线:如果sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。
哨兵选主规则:
首先判断主与从节点断开时间长短,如超过指定值就排该从节点
然后判断从节点的slave-priority值,越小优先级越高。
如果slave-priority一样,则判断slave节点的offset值,越大优先级越高、
最后是判断slave节点的运行id大小,越小优先级越高。
面试问题:
分片集群结构
主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决;
海量数据存储问题
高并发写的问题
使用分片集群可以解决上述问题,分片集群特征:
集群中有多个master,每个master保存不同的数据
每个master都可以有多个slave节点
master之前通过ping检测彼此健康状态
客户端请求可以访问集群任意节点,最终都会被转发到正确节点。
分片集群结构-数据读写:
Redis分片集群引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。
面试问题:
Redis是单线程的,但是为什么还那么快
Redis是纯内存操作,执行速度非常快
采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题
采用I/O多路复用模型,非阻塞I/O
解释一下I/O多路复用模型?
Redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度。I/O多路复用模型主要是实现了高效的网络请求
用户空间和内核空间
常见的IO模型
阻塞IO(Blocking IO)
非阻塞IO(Nonblocking IO)
IO多路复用(IO Multiplexing)
Redis网络模型
IO多路复用:selelct模式一次性传递多个socket进程,只要有就绪的数据就会返回readable然后调用recvfrom函数,IO和阻塞IO直接调用recvfrom函数
IO多路复用:
IO多路复用是利用单个线程来同时监听多个Socket,并在某个Socket可读,可写时得到通知,从而避免无效的等待,充分利用CPU资源。不过监听Socket的方式,通知的方式又有多种实现,常见的有:
select
poll
epoll
差异:
select和poll只会通知用户进程有Socket就绪,但不确定具体是哪个Socket,需要用户进程逐个遍历Socket来确认
epoll则会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间
面试题: