缓存穿透

缓存穿透是指查询一个不存在的数据,数据库也不会直接写入缓存,从而每次都会访问数据库的问题

缓存穿透解决方案:

方案一:缓存空数据

将查询为null的结果写回到缓存中 比如:{key:1,value:null}

方案二:布隆过滤器

布隆过滤器(Bloom Filter)是一种数据结构,它包含一系列哈希函数以及一个固定大小的二进制数组,这种过滤器常用于检测一个元素是否存在于一个大型集合中。

在布隆过滤器中,查询某个变量是否存在时,首先将该变量经过一系列哈希函数映射到二进制数组对应位置查看该位置值是0还是1,如果任意一个是0则表示该变量不在集合中,如果都是1则表示该变量很有可能存在于集合中。但需要注意由于哈希碰撞这种判断并非绝对正确。

 

采用布隆过滤器避免缓存穿透的步骤如下

 

缓存击穿

 缓存击穿是指某一个key过期时,恰巧有大量对这个key的请求都打到db上,这些并发请求可能瞬间把db压垮

 

缓存击穿解决方案

方案一:在访问db前设置互斥锁

互斥锁是指同一时间只有一个线程可以访问共享资源的一种同步机制。

如下图所示,采用互斥锁避免缓存击穿是当缓存失效时不会立即去load db,而是先使用如redis的setnx去设置一个互斥锁,某个线程获取到互斥锁时在进行db操作,而那些没有获得互斥锁的线程只能重试下图中步骤1/2操作

采用互斥锁避免缓存击穿的优势是由于只有一个线程获取互斥锁这样保证了数据的强一致性,但由于其他线程处于获取互斥锁失败进而一直重试从而导致性能较差

 

 方案二:逻辑过期

 正常情况下,设置redis有效时间格式为:redisTemplate.opsForValue(key,value, expireTime,TimeUnit.xxx),其中expire Time便是过期时间,timeUnit是事件类型(时/分/秒).

但是,逻辑过期策略是上边的expireTime过期时间不设置值这样对应key的数据便没有过期时间了,但是在value中额外增加一个逻辑过期时间(比如某个过期时间戳)。当应用程序读取数据时由于key对应的过期时间永不过期所以首先会访问reids,然后在去找key对应value中找出逻辑过期时间进而判断这个逻辑过期时间是否过期;如果逻辑过期时间没有过期则直接返回,若过期则触发新线程(或者异步任务)刷新缓存数据,并且原来线程直接返回旧的缓存数据

 如下图

线程1查询缓存逻辑过期时间发现已过期,则尝试获取互斥锁并且获取成功,返回旧数据的同时建立一个新的线程来进行缓存刷新;新的线程3此时再查询时也发现逻辑过期时间过期了,尝试获取互斥锁失败便直接返回旧数据。直到线程2刷新缓存成功并且释放锁成功后,再次查询才能获取到最新的缓存数据。所以根据如下图所示,采用逻辑过期时间策略只有线程4返回的数据才是最新的。

逻辑过期策略是过期后不需要任何等待直接返回旧数据,这样性能好但是不保证数据的一致性

 参考文献: https://blog.csdn.net/m0_64064862/article/details/148253540

缓存雪崩

缓存雪崩是某一时间段大量key同时过期或者reids宕机,导致大量请求打到db

 解决方案一: 给不同key设置不同的过期时间

解决方案二:利用Redis集群提高服务的高可用性这样可以避免某个redis节点宕机发生缓存雪崩的情形

解决方案三:给缓存业务添加限流策略

解决方案四:给业务添加多级缓存

  采用Guava/Caffeine这种本地缓存

缓存雪崩Vs缓存击穿

缓存雪崩是当量不同的key同时失效,缓存击穿是某个key失效

posted on 2025-06-07 08:43  colorfulworld  阅读(45)  评论(0)    收藏  举报