Redis
1.Redis的使用场景
- 缓存
- 缓存三兄弟(穿透、击穿、雪崩)
- 双写一致
- 持久化
- 数据过期策略
- 数据淘汰策略
- 分布式锁
- setnx
- redisson
- 消息队列、延迟队列
- 何种数据类型
2.什么是缓存穿透,怎么解决
- 缓存穿透:查询一个不存在数据,MySQL查询不到数据也不会直接写入缓存,就会导致每次请求都查数据库,这种情况大概率是遭到了攻击。
- 解决方案:
- 1.缓存空数据:查询结果为空时,仍把这个空结果存入缓存。
- 优:简单
- 缺:消耗内存,可能发生不一致的问题
- 2.布隆过滤器
- 1.缓存空数据:查询结果为空时,仍把这个空结果存入缓存。
什么是布隆过滤器?
- 布隆过滤器:用于检索一个元素是否在一个集合中,拦截不存在的数据。我们当时使用的是redisson实现的布隆过滤器。
- 底层原理:
- 初始化一个bitmap(位图)的数组,默认为0
- 存储时通过多次hash计算,根据哈希值将对应数据改为1
- 查找时根据计算的hash判断位置是否为1
- 缺:可能会产生一定的误判、实现复杂
- 解决方案:
- 1.设置误判率,不会超过5%
- 2.增加数组的长度
误判率:数组越小误判率越大,数组越大误判率越小,但同时带来了很大内存消耗
3.什么缓存击穿,怎么解决
- 缓存击穿:给某个key设置了过期时间,当key过期时,恰好这时对这个key有大量的并发请求,这些并发请求可能会瞬间将DB压垮
- 解决方案:
- 1.互斥锁,强一致性,性能差
- 2.逻辑过期,高可用,性能优,不能保证数据绝对一致
4.什么时缓存雪崩,怎么解决
- 缓存雪崩:在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力
- 解决方案:
- 1.给不同的key的TTL添加随机值,让失效时间重复率降低
- 2.利用Redis集群提高服务的可用性
- 哨兵模式
- 集群模式
- 3.给缓存业务添加降级限流策略(是保底策略,适用穿透、击穿、雪崩)
- ngxin
- spring cloud gateway
- 4.给业务添加多级缓存
- Guava
- Caffeine
《缓存三兄弟》
穿透无中生有key,布隆过滤null隔离
缓存击穿过期key,锁与非期解难题
雪崩大量过期key,过期时间要随机
面试必考三兄弟,可用限流来保底
5.Redis作为缓存,MySQL的数据如何与Redis进行同步(双写一致性)
- 双写一致性:当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要求要一致
- 读操作:缓存命中,直接返回;缓存未命中查询数据库,写入缓存,设定超时时间
- 写操作:延迟双删
1.什么使延时双删?
先删缓存,再修改数据库,再延时后删除缓存
2.先删缓存还是先修改数据库?
都会导致脏数据的出现,使数据库与缓存不一致
3.为什么要删除两次缓存?
降低脏数据的出现
4.为什么要延时删除?
一般是为了等待主从节点同步数据,防止脏数据出现
延时双删可以极大控制风险,但是延时时间不好确定,依旧有脏数据风险
-
1.业务背景:一致性要求高(抢券)
-
解决方案:
- 共享锁:读锁readLock,加锁后,其他线程可以共享读操作
- 排他锁:也叫独占锁writeLock,加锁后,阻塞其他线程读写操作
-
2.业务背景:允许延迟一致
-
解决方案:异步通知保证数据最终一致性
- 1.使用MQ中间件:更新数据后,通知缓存删除,需要MQ的可靠性
- 2.使用Canal中间件:基于MySQL的主从同步来实现的。不需要修改业务代码,伪装为MySQL的一个从节点,通过读取binlog数据更新缓存
6.Redis做为缓存,数据的持久化怎么做
- RDB:数据快照,把Redis内存存储的数据写到磁盘上,当Redis实例宕机恢复数据的时候,方便从RDB的快照文件中恢复数据
- AOF:追加文件,当Redis操作写命令的时候,都会存储到这个文件中,当Redis实例宕机恢复数据的时候,会从这个文件中,再执行一遍命令来恢复数据
- 二者对比:数据完整性,文件大小,宕机恢复速度,数据恢复优先级,系统资源占用,使用场景
7.Redis中的key过期后,会立即删除吗(数据过期策略)
- 数据过期策略:Redis对数据设有数据的有效时间,数据过期以后就要从内存中删除掉,删除时要有数据删除规则,这种删除规则就是数据过期策略
- 惰性删除:访问key时判断是否过期,过期了就删除
- 优:对cpu友好,不浪费性能
- 缺:对内存不友好,容易积攒
- 定期删除:定期随机删除一定量的key中过期的key
- 1.slow模式:定时任务,执行频率10hz,每次不超过25ms,以通过修改配置文件redis.conf的hz选项来调整这个次数
- 2.fast模式:执行频率不固定,每次事件循环都会尝试执行,但两次间隔不低于2ms,每次耗时不超过1ms
- 优:既能通过限制时长和频率减少操作对cpu影响,也能定时释放过期占用的内存
- 缺:难以确定删除操作时长和频率
8.假如缓存过多,内存是有限的,内存占满了怎么办(数据淘汰策略)
- 数据淘汰策略:当Redis的内存不够用时,此时再向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据淘汰掉
- noeviction:不淘汰任何key,内存满了不写入新数据(默认)
- volatile-ttl:对所有TTL的key,TTL剩余的越少越先淘汰
- allkeys-random:全体,随机淘汰
- volatile-random:有TTL的,随机淘汰
- allkeys-lru:全体,LRU(最近最少使用)淘汰
- volatile-lru:有TTL的,LRU(最近最少使用)淘汰
- allkeys-lfu:全体,LFU(最少频率使用)淘汰
- volatile-lfu:有TTL的,LFU(最少频率使用)淘汰
9.Redis分布式锁,是如何实现的
- 使用场景:集群情况下定时任务,抢单,幂等性
- setnx:如果不存在则SET
- redisson:底层实现是使用了setnx和lua脚本(保证原子性)
1.Redisson实现的分布式锁如何合理控制锁的有效时长?
在redisson中提供了WatchDog(看门狗),一个线程获取锁成功以后,WatchDog会给持有锁的线程续期(默认时每隔十秒续期一次)
由于高并发下,业务执行的很快,新来的业务想要获取锁,不会马上被拒绝,会自动不断尝试,性能得到一定的提升
2.Redisson锁可以重入吗?
可以,多个锁重入需要判断是否是当前线程,在redis中进行存储的时候使用hash结构,来存储线程信息和重入的次数
3.Redisson可以解决主从数据一致的问题吗?
不可以,但是可以使用redisson提供的红锁来解决,但是这样的话,性能就太低了,如果非要保证数据的强一致性,建议采用zookeeper实现的分布式锁
10.Redis集群有哪些方案
- 主从集群
1.介绍一下redis的主从同步
单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,搭建主从集群,实现读写分离。一般是一主多从,主节点负责写数据,从节点负责读数据
2.说一下主从同步数据的流程
全量同步
增量同步
- 哨兵模式
- 功能: 监控、自动故障恢复、通知
- 服务状态监控:主观下线、客观下线
- 哨兵选主规则:断开时间、slave-priority、offset值、运行id大小
1.脑裂问题怎么解决
由于网络等原因,使master和salve、sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到master,所以通过选举的方式提升了一个salve为master,这样就存在了两个master,就像大脑分裂了一样,这样会导致客户端还在old master那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会将old master降为salve,这时再从新master同步数据,这会导致old master中的大量数据丢失。
解决方案:
1.设置最少的salve节点个数
2.设置主从数据复制和同步的延迟时间,达不到要求就拒绝请求
- 分片集群:解决的是,海量数据存储的问题
- 功能:
- 1.集群中有多个master,每个master保存不同数据
- 2.每个master设置多个slave节点,就可以继续增大集群的高并发能力
- 3.每个master之间通过ping监测彼此健康状态,就类似于哨兵模式了
- 4.客户端请求可以访问集群任意节点,最终都会被转发到正确节点
- 读写数据:哈希槽
- 有 16384 个哈希槽
- key通过 CRC16 校验后对 16384 取模来决定放置哪个槽,通过槽找到对应的节点进行存储
- 功能:
11.Redis是单线程为什么还那么快
- 纯内存
- 单线程,避免不必要的线程上下文切换和安全问题
- I/O多路复用模型,非阻塞值I/O
1.解释一下I/O复用模型
Redis的性能瓶颈是网络延迟,I/O多路复用模型主要就实现了高效的网络请求
用户空间和内核空间
常见IO模型
- 阻塞IO:等待和拷贝都阻塞
- 非阻塞式IO:拷贝阻塞
- IO多路复用:利用单线程来监听多个Socket,并在某个Socket可读,可写时得到通知,从而避免无效的等待,充分利用cpu资源
- 实现方式:
- select,poll:不知道已就绪的Socket是哪一个,要逐渐遍历Socket来确认
- epoll:在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能。
Redis网络模型:使用I/O多路复用结合事件的处理器来应对多个Socket请求
- 连接应答处理器
- 命令回复处理器:在Redis6.0之后,使用了多线程来处理回复事件
- 命令请求处理器:在Redis6.0之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程