小仙女讲Redis(2)——问题与解决

请问大家,在使用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只允许一个线程查询数据和写缓存,其他线程等待。

发布了258 篇原创文章 · 获赞 769 · 访问量 34万+

猜你喜欢

转载自blog.csdn.net/qsbbl/article/details/99610713