Redis 三大灾难以及解决办法

Redis 三大灾难(雪崩 穿透 击穿)

缓存穿透

缓存穿透:缓存和数据库中都没有的数据,可用户还是源源不断的发起请求,导致每次请求都会到数据库,从而压垮数据库
😀😀😀 老规矩先上图
正常:在这里插入图片描述

解决方案

在这里插入图片描述
有很多种方法可以有效地解决缓存穿透问题,

布隆过滤器
  • 最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

在这里插入图片描述
当业务系统有查询请求的时候,首先去BloomFilter中查询该key是否存在。若不存在。则说明数据库中也不存在该数据,因此缓存都不重要了,直接返回null。若存在,则继续执行后续的流程,先前往缓存中查询,缓存中没有的话再前往数据库中的查询。
  它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难

缓存空值
  • 另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,当后续又出现该key的查询请求时,缓存直接返回null,而无需查询数据库。但它的过期时间会很短,最长不超过五分钟。
int cacheTime = 30;
String cacheKey = "product_list";

String cacheValue = CacheHelper.Get(cacheKey);
if (cacheValue != null) {
    return cacheValue;
}

cacheValue = CacheHelper.Get(cacheKey);
if (cacheValue != null) {
    return cacheValue;
} else {
    //数据库查询不到,为空
    cacheValue = GetProductListFromDB();
    if (cacheValue == null) {
        //如果发现为空,设置个默认值,也缓存起来
        cacheValue = string.Empty;
    }
    CacheHelper.Add(cacheKey, cacheValue, cacheTime);
    return cacheValue;
}
两种方法的比较
  • 对于一些恶意攻击,查询的key往往各不相同,而且数据贼多。此时,第一种方案就显得提襟见肘了。因为它需要存储所有空数据的key,而这些恶意攻击的key往往各不相同,而且同一个key往往只请求一次。因此即使缓存了这些空数据的key,由于不再使用第二次,因此也起不了保护数据库的作用。 因此,对于空数据的key各不相同、key重复请求概率低的场景而言,应该选择第二种方案
  • 而对于空数据的key数量有限、key重复请求概率较高的场景而言,应该选择第一种方案。

缓存击穿

缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮

解决方案

使用互斥锁(mutex key)

业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

"提前"使用互斥锁(mutex key):

在value内部设置1个超时值(timeout1), timeout1比实际的memcache timeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长 timeout1并重新设置到cache。然后再从数据库加载数据并设置到cache中。

“永远不过期”

这里的“永远不过期”包含两层意思:

(1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。

(2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期

expire key time(以秒为单位)--这是最常用的方式

setex(String key, int seconds, String value)--字符串独有的方式
资源保护:

采用netflix的hystrix,可以做资源的隔离保护主线程池,如果把这个应用到缓存的构建也未尝不可

缓存雪崩

缓存雪崩就是指缓存由于某些原因(比如 宕机、cache服务挂了或者不响应)整体crash掉了,导致大量请求到达后端数据库,从而导致数据库崩溃,整个系统崩溃,发生灾难。
雪崩过程

  • redis集群彻底崩溃

  • 缓存服务大量对redis的请求hang住,占用资源

  • 缓存服务大量的请求打到源头服务去查询mysql,直接打死mysql

  • 源头服务因为mysql被打死也崩溃,对源服务的请求也hang住,占用资源

  • 缓存服务大量的资源全部耗费在访问redis和源服务无果,最后自己被拖死,无法提供服务

  • nginx无法访问缓存服务,redis和源服务,只能基于本地缓存提供服务,但是缓存过期后,没有数据提供

  • 网站崩溃

解决方案

加锁排队. 限流-- 限流算法. 1.计数 2.滑动窗口 3. 令牌桶Token Bucket 4.漏桶 leaky bucket [1]
  • 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
  • 业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。
    SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。
数据预热

可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀
.做二级缓存,或者双缓存策略。

做二级缓存,或者双缓存策略。
  • A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。
  • 缓存永不过期 同上
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值