Redis中缓存穿透、缓存击穿和缓存雪崩

1、数据的查询过程

程序在处理用户请求时候,一般先查询缓存中是否存在用户的查询数据,如果缓存中不存在再去查询数据库,如果在数据库中查询到了数据,在把数据返回给用户同时把数据放入缓存中;如果在数据库中也没有查询到数据,则返回空值并且没有数据放入到缓存中。当用户的大量的数据请求都不在缓存中时,会对数据库造成较大压力,尤其是有恶意用户发起恶意攻击时。

2、缓存穿透

(1)what

查询一个一定不存在的数据值,由于缓存不命中所以需要从数据库查询,数据库中查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,失去了缓存的意义,造成缓存穿透。查询的是一定不存在的数据,所以缓存中查不到,所以一定会去访问数据库,失去了缓存减缓数据库访问压力的作用)

(2)解决方

a、将可能出现的缓存key的组合方式的所有数值以hash形式存储在一个很大的bitmap中<布隆过滤器>(需要考虑如何将这个可能出现的数据的hash值之后同步到bitmap中, eg. 后端每次新增一个可能的组合就同步一次,或者 穷举),一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力

b、常用: 如果对应在数据库中的数据都不存在,我们将此key对应的value设置为一个默认的值,比如“NULL”,并设置一个缓存的失效时间。当然这个key的时效比正常的时效要小的多

3、缓存击穿

(1)what

与缓存穿透不同,缓存击穿中的热点key在数据库中是存在的,在缓存中也存在过,只是在这些热点数据失效的瞬间,发起了大量对热点数据的请求,导致缓存中不命中直接访问数据库,造成对数据库的巨大压力。

扫描二维码关注公众号,回复: 10526524 查看本文章

(2)解决方案

a、通过synchronized+双重检查机制:某个key只让一个线程查询,阻塞其它线程在同步块中,继续判断检查,保证不存在,才去查DB

b、设置value永不过期,这种方式可以说是最可靠的,最安全的但是占空间,内存消耗大,并且不能保持数据最新 这个需要根据具体的业务逻辑来做 

c、使用互斥锁(mutex key),业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。
 

4、缓存雪崩

(1)what

redis服务器挂掉,多个key查询并且出现高并发,缓存中失效或者查不到,然后都去db查询,导致请求大量涌至数据库,使得db压力突然飙升,从而崩溃。

(2)解决方案

a、在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。(跟击穿的第一个方案类似,但是这样是避免不了其它key去查数据库,只能减少查询的次数)
b、可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存
    不同的key,设置不同的过期时间,具体值可以根据业务决定,让缓存失效的时间点尽量均匀
c、做二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。(这种方式复杂点)

发布了19 篇原创文章 · 获赞 0 · 访问量 252

猜你喜欢

转载自blog.csdn.net/wangqsse/article/details/105127225