Redis缓存击穿、穿透、雪崩及解决方案

1、缓存处理流程

接收到查询数据请求时,优先从缓存中查询,若缓存中有数据,则直接返回,若缓存中查不到则从DB中查询,将查询的结果更新到缓存中,并返回查询结果,若DB中查不到,则返回空数据

img

​ 缓存处理流程图

2、缓存穿透(Cache Penetration)

当缓存与数据库中都不存在该数据时,由于数据库查询不到数据就不会写入缓存,这个时候如果用户不断的恶意发起请求,就会导致这个不存在的数据每次请求都会查询DB,请求量大的情况下,就会导致DB压力过大,直接挂掉。

​ 缓存穿透:用户不断发起缓存和数据库中都不存在的请求

解决方案:

1、不存在数据设置短过期时间:当查询返回一个空数据时,直接将这个空数据存到缓存中,过期时间不宜设置过长,建议不超过5分钟

简单的来说,就是请求之后,发现数据不存在,就将null值打入Redis中。

优点:实现简单,维护方便

缺点:额外的内存消耗、可能造成短期的不一致

img

2、 业务层校验:可以在查询缓存之前,先对请求的参数进行合法性检查,如过滤非法字符、 判断参数范围等,对于明显错误的参数,直接拦截返回

3、采用布隆过滤器:将所有可能存在数据,分别通过多个哈希函数生成多个哈希值,然后将这些哈希值存到一个足够大的bitmap中,此时一个一定不存在的数据就会被这个bitmap拦截,从而减少了数据库的查询压力。

优点:内存占用较少,没有多余 key

缺点:实现复杂 存在误判可能

img

3、缓存击穿(Cache Breakdown)

某一个数据缓存中没有但数据库中有的数据(一般是缓存时间到期/缓存中某个热点数据失效),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,严重情况下会直接挂掉。

​ 缓存击穿:热点key失效的瞬间大量请求进来

解决方案:

1、设置热点数据永不过期:对于某个需要频繁获取的信息,缓存在Redis中,并设置其永不过期

物理上的不过期:不设置TTL

“逻辑上”的不过期(缓存到期动态构建缓存)

之前所说导致缓存击穿的原因就是该key的TTL到期了,所以我们在这就不设置TTL了,
而是使用一个字段,例如:expire表示过期时间(逻辑上的)。当我们想让它 “ 过期 ” 的时候,
我们可以直接手动将其删除(热点key,即只是在一段时间内,其被访问的频次很高)。

这种方案巧妙在于,异步的构建缓存,缺点在于在构建完缓存之前,返回的都是脏数据。

img

2、定时更新:比如这个热点数据的过期时间是1h,那么每到59minutes时,通过定时任务去更新这个热点key,并重新设置其过期时间。

3、添加锁

  • 互斥锁:互斥锁简单来说就是在Redis中根据key获得的value值为空时,先锁上,然后从数据库加载,加载完毕,释放锁。若其他线程也在请求该key时,发现获取锁失败,则睡眠一段时间(比如100ms)后重试

    简单的来说:并不是所有的线程都有 “ 资格 ” 去访问数据库,只有持有锁的线程才可以对其进行操作。

    不过该操作有一个很明显的问题,就是会出现相互等待的情况。

    img

  • 根据key值加锁:这样线程之间会不影响,不会因为某一个线程获取了锁,其它线程就处于等待时间,也就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据

解决方案 优点 缺点
互斥锁 没有额外的内存消耗
保证一致性
实现简单
线程需要等待,性能受影响
可能有死锁风险
逻辑过期 线程无需等待,性能较好 不保证一致性
有额外内存消耗
实现复杂

4、缓存雪崩(Cache Avalanche)

缓存中大批量的数据都到了过期时间,从而导致查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同,缓存击穿是指某一条数据到了过期时间,大量的并发请求都来查询这一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库

img

解决方案:

1、给不同的Key的TTL添加随机值(推荐):缓存数据的过期时间设置随机,可以在原有的过期时间上加上一个随机值,比如1-3min,防止同一时间大量缓存数据集体失效,导致数据库压力过大

2、给业务添加多级缓存:可以采用多级缓存架构,请求到达浏览器,nginx可以做缓存,未命中找Redis,最后到数据库......减少缓存层的压力

3、可以在缓存层和数据库层之间添加限流、熔断等措施:避免因突发流量导致系统崩溃

posted @ 2025-02-26 15:23  搁浅~浅浅浅  阅读(103)  评论(0)    收藏  举报