目录
一、缓存穿透
情景再现:
当用户请求服务器查找数据,通常操作比较频繁的数据我们会在Redis里面去查询,如果Redis没有,按理来说我们应该去数据库查,查完之后再同步到Redis中去。如果在数据库里也查不到就无法同步到缓存中去,造成缓存穿透
示意图:
缓存穿透的原因:
1.key对应数据的数据源不存在,每次针对此key的请求从缓存中都获取不到,都会压到数据源,就可能压垮数据源
2.比如:我们用一个不存在的用户id查询信息,数据库和缓存都没有,黑客就可以利用此漏洞进行攻击
3.也就是说,如果从存储层查不到数据则不会写入缓存,这将导致这个不存在的数据每次都要去存储层查询,失去了缓存的意义 ,频繁查询就会压垮数据库
缓存穿透的现象:
1.应用服务器压力变大
2.Redis命中率低
3.一直查数据库
缓存穿透的解决办法:
1.对空值缓存
如果一个查询返回的数据是空,我们仍然把这个空结果(null)进行缓存,设置空结果的时间应该短一些(小于5min),因为也可能是临时没有的
2.设置可访问的白名单
定义一个可访问的白名单,每次访问的名单和白名单id进行比较,如果访问id不在白名单里面就进行拦截不允许访问,可以通过bitmaps实现
3.采用布隆过滤器
布隆过滤器可以检索一个元素是否在集合中。他的优点就是空间效率和时间效率都远远高要求其他算法,缺点是有一定的误识别率和删除困难
4.进行实时监控
当发现Redis命中率急速降低的时候,需要排查访问对象和数据,和运维人员配合,设置黑名单限制服务
二、缓存击穿
情景再现:
当用户请求服务器查找数据,但是key过期了就会越过Redis去访问数据库,导致数据库瞬间访问量过大
示意图:
缓存击穿的原因:
1、key对应的数据存在,但在Redis中过期,此时如果大量的并发请求过来,就会发现缓存过期,于是从DB加载数据并且回到Redis,这个大量的并发请求就可能把DB压垮
2、比如某一个热点数据,在某个时间点瞬间被超高并发的访问就会压垮数据库,出现缓存击穿
缓存击穿的现象:
1、数据库访问压力瞬间增加
2、Redis里面没有出现大量key过期
3、Redis正常运行,数据库瘫痪了
缓存击穿的解决办法:
1、预先设置热门数据
在redis高峰访问之前、把这些热门数据加到redis里面去,加大热门数据的key时长
2、实时调整
现场监控哪些热门数据,加大key时长
3、使用锁
给数据库设置一个排它锁,当用户从redis中查不到key对应的数据(返回为null),就去数据库查,此时会上锁,只有单线程通过,拿到数据之后同步到redis中,此时如果其他线程来访问就会休眠一段时间然后重新访问redis。最终拿到数据
1)就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db。
2)先使用缓存工具的某些带成功操作(比如Redis的SETNX)去set一个mutex key
3)当操作返回成功时,再进行load db的操作,并回设缓存,最后删除mutex key;
4)当操作返回失败,证明有线程在load db,当前线程睡眠一段时间再重试整个get缓存的方法
5)使用锁,效率会有影响
三、缓存雪崩
情景再现:
当用户发送web请求到Nginx,Nginx转发大量请求到对应的服务器,然后大量的请求访问缓存,如果缓存数据大量过期,就会去数据库更新。如果此时出现缓存雪崩,那么从数据库更新数据就会失败
示意图:
缓存雪崩的原因:
1、大量的并发请求去请求不同的key,key对应的数据在redis中大量过期,然后通常会从DB加载数据并回设到缓存,但此时大并发请求就会把DB压垮。
2、缓存击穿针对一个key,缓存雪崩针对很多key
缓存雪崩的现象:
1、数据库访问压力过大,服务器崩溃
2、在极短的时间内,访问大量的key,而这些key集中过期
缓存雪崩的解决办法:
1、构建多级缓存架构
nginx架构+redis缓存+其他缓存(ehcache等),这种方式开发/维护成本高
2、使用锁或者队列
用加锁或者队列的方式来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求到底层存储系统上
3、设置过期时间更新缓存
记录缓存数据是否过期,如果过期会触发通知另外的线程在后台去更新实际key的缓存,让key不要集中过期,保证key一直在redis。
4、将缓存失效时间分散开
可以在原有的失效时间上加一个随机值(1min~5min),分散开就可以