请问大家,在使用redis的过程中有没有遇到过什么问题呢?比如缓存雪崩,缓存穿透,阻塞等。这些问题的产生原因是什么呢?又该怎么解决呢?本篇文章就说说这个。
阻塞
因为redis是单线程架构,所有的读写操作都是在一条主线程中完成的,所以一旦出现阻塞,将是致命的。
内在原因
(1)API或数据结构使用不合理
// 获取最近的10条慢查询
slowlog get 10
(2)CPU饱和
(3)持久化相关的阻塞:fork阻塞、aof刷盘阻塞、hugepage写操作阻塞
外在原因
(1)CPU竞争
(2)内存交换
(3)网络问题:连接拒绝、网络延迟、网卡软中断
解决措施
(1)在应用方加入异常监控,并要定位到具体的出问题的结点
(2)Redis的监控
缓存相关
更新策略
redis中的数据通常都是有生命周期的,需要在特定时间后被删除或更新,这样可以保证缓存空间在一个可控的范围。有3种缓存更新策略。
(1)LRU/LFU/FIFO算法剔除
当缓存使用量超过了预设的最大值时,会启动策略对数据进行清除。
LRU:最近最久未使用
LFU:最近最少使用
FIFO:先进先出
(2)超时剔除
在将数据放入缓存时,已经设置了过期时间,当到时间后,自动将其删除。使用命令expire来实现。
(3)主动更新
对数据进行“增删改”操作后,立即清除redis中的相关数据
缓存穿透
1、概念
缓存穿透是指查询一个数据库不存在的数据,每次都去查询数据库,而每次查询都是空,每次又都不会进行缓存。假如有大量请求进来,可能会压垮数据库。
2、产生的原因
(1)自身业务代码或者数据出现问题
(2)一些恶意攻击,爬虫等造成大量空命中
3、解决措施
(1)缓存空对象
当数据库不命中后,仍然将空对象保留到缓存层中,之后再访问这个数据,将会从缓存中获取,这样就保护了后端数据源。
实现代码如下:
String get(String key){
//从缓存中获取数据
String cacheValue=cache.get(key);
//缓存为空
if(StringUtils.isBlank(cacheValue)){
//从存储中获取
String storageValue=storage.get(key);
cache.set(key,storageValue);
//如果存储数据为空,需要设置一个过期时间(300秒)
if(storageValue==null){
cache.expire(key,60*5);
}
return storageValue;
}else{
//缓存非空
return cacheValue;
}
}
此解决办法的使用场景为:数据命中不高且数据频繁变化实时性高。
(2)布隆过滤器拦截
将存在的key放入布隆过滤器中。当一个查询请求过来时,先经过此过滤器,如果此过滤器认为该数据不存在,就直接丢弃,不再继续访问缓存层和存储层。
此解决办法的适用场景为:数据命中不高,数据相对固定、实时性低
无底洞
1、概念
为了满足业务需求,大量添加redis集群的节点,但是性能没提升反而下降。
2、产生的原因
在单机操作中,如果想批量获取多个key,只需一次网络操作即可,在集群中,节点越多,涉及的网络IO就越多,性能就可以能会下降。
3、解决办法
(1)串行命令
(2)串行IO
(3)并行IO
(4)hash_tag实现
缓存雪崩
1、概念
缓存雪崩是指在设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,导致所有的查询都落在数据库上,造成了缓存雪崩。
2、解决办法
(1)不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
(2)如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。
(3)设置热点数据永远不过期。
(4)做二级缓存。
(5)在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。