缓存雪崩、击穿、穿透

96 阅读4分钟

前言

作为后端开发人员,我们深知数据库在高业务qps下显得相对脆弱。为了应对高流量压力,我们常常会引入Redis缓存机制,然而这一举措却引发了缓存雪崩、缓存击穿以及缓存穿透等三大问题。本文将详细探讨这些问题,并提供相应的解决方案。

缓存雪崩

缓存雪崩是指在缓存系统中大量的缓存数据同时过期或者Redis本身故障,导致大量的请求直接访问数据库或后端系统,造成服务器负载剧增,甚至引发系统崩溃的现象。通常情况下,缓存的过期时间设置相近,当某一时刻这些缓存同时过期或失效时,原本应该被缓存的数据都会直接请求后端系统,导致集中的请求压力,使得数据库无法承受巨大的并发请求而崩溃。

image.png

解决办法

1.既然是大量缓存同时过期,我们在给缓存设置过期时间时配置随机时间,从而避免大量缓存在同一时间过期。

2.加锁,不是说大量请求打到数据库来吗?我们加个互斥锁,请求数据库前必须获取到这个锁,没有拿到锁的线程可以等一会或者返回空值/默认值。

3.不设置过期时间,但redis由于异常重启、内存淘汰策略等的存在,不设置过期时间并不意味着缓存会一直存在,当发现缓存不存在可以再互拆锁或者使用消息队列,保证同一时刻最多只有一条或者少量请求打到数据库。

4.针对redis异常故障宕机(这种概率极低),我们可以构建redis高可用集群,有必要的话可以加入限流熔断措施。

缓存击穿

缓存击穿是指针对某一特定的缓存键(key),由于并发请求中有大量针对该键的请求,但是该键对应的缓存数据恰好过期失效或不存在,导致大量请求穿透缓存直接访问数据库。与缓存雪崩不同的是,缓存击穿针对的是某个热点数据的缓存失效问题。由于大量请求同时访问缓存中已失效的数据,这些请求无法被缓存拦截,直接访问数据库,给数据库带来了巨大的压力。

image.png

解决办法

1.其实缓存击穿和缓存雪崩有很多相似的地方,同样可以用到前面说到的互斥锁方案来解决。

2.这里还有一种比较巧妙的处理办法,设置逻辑过期时间,我们的key本身不过期,但为了保证缓存和数据库的一致性,在value中设置缓存的逻辑过期时间,当发现逻辑过期时间过期后,抢夺互斥锁从数据库读取最新的数据更新到缓存中,未争抢到锁的线程可以根据业务对数据的一致性容忍度换回旧值。

缓存穿透

缓存穿透是指恶意或非法请求频繁地查询缓存中不存在的数据,导致这些请求直接绕过缓存层,直接访问数据库或后端系统。通常情况下,这些请求所查询的数据不存在于缓存中,也不存在于数据库中,但由于恶意请求的频繁访问,使得系统不断地尝试去查询这些无效的数据,浪费了大量的计算资源和数据库连接资源。

image.png

解决办法

1.出现缓存穿透一般都是遇到了恶意刷接口,根据业务的不同,如电商或者金融领域,可以引入风控措施,针对恶意请求进行ip、账户等的封禁。

2.缓存空值或者默认值,当数据库数据也不存在时,我们可以在缓存中存取空值或者默认值。

3.布隆过滤器(不推荐),使用布隆过滤器可以快速的判断数据是否存在。但会存在误判,由于哈希冲突的原因,布隆过滤器说数据存在,并不一定存在,而且数据在删除后,需要重建布隆过滤器,代价较高,所以这种方式并不太推荐。

针对缓存击穿和缓存穿透这两个问题,作为开发人员其实并不知道哪个key是否真的存在,所以缓存击穿和穿透看似是两个问题,但在实际场景中,他们更像是同一个问题,所以我们一般通用的处理方案是:

  • 1.缓存不存在,抢锁,没抢到就等或者返回默认值;
  • 2.抢到了再次检查缓存数据有没有有效的数据(双重检查),没有再去查数据库,有数据就设置逻辑过期时间放到缓存中,没数据就放空值;
  • 3.缓存存在,但是逻辑过期了,抢锁,没抢到返回默认值;