PHPer不孤独

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

2020-08-28  本文已影响0人  川川相护

话不多说先上生动的例子:

我们有个pv上亿的活动页面,页面上好多信息都缓存在redis中,碰巧我们设置的key的过期时间都是12点。在key过期的一瞬间,我有个活动开奖,大量用户涌入该页面,假设当时每秒5000个请求,原本缓存可以扛住每秒4000个请求,但是缓存失效。此时5000个请求全部落到数据库,数据库一下就报警了,此时DBA没反应过来就直接挂了。这个时候如果没有预案,直接重启数据库,新的请求会不断涌进来,数据库立马又被打挂,这就是一种缓存雪崩的表现。

如果雪崩发生在核心用户服务模块,同一时间大面积失效,redis形同于无,这个数量级的请求直接打到数据库,此时如果没有做熔断降级,所有依赖用户服务的接口几乎都会报错,只要用户请求没有减少,无论怎么重启,都是直接被打挂,等能重启的时候,用户估计早就卸载app了,甚至感叹一句,这什么垃圾产品..

那我们应该如何应对缓存雪崩的问题?

1.批量写入redis缓存数据的时候,把每个key的失效时间都加一个随机值,这样可以保证数据不会在同一时间大面积失效。

setRedis(Key,value,time+Math.random()*1000);

2.redis集群,将热点数据分布在不同的redis库中

3.设置热点数据永不过期,有更新操作就更新缓存

缓存穿透:

缓存穿透是指缓存和数据库中都没有的数据。例如一个攻击者查询一个不可能在数据库中存在的id值,高频的请求会导致数据库压力过大,严重会打挂数据库。

穿透

如果一直用小于0的参数去请求,每次在redis中都查不到,会直接打到数据库,数据库也查不到,此时并发足够高就容易打崩db。

穿透的解决方案:

1.布隆过滤器

利用高效的数据结构和算法快速判断出key是否在数据库中,不存在就return。

2.接口层对参数进行校验,不合法的参数直接代码过滤return,比如id<=0则直接return

理论上对服务端来说,前端的所有参数都是不被信任的,我们要考虑到参数的所有可能情况,并作出合理校验。

在缓存和数据库中都没有查到的数据,可以缓存一个flag,但是缓存时间不应设置过长,以免影响正常使用。这样可以防止攻击者频繁使用同一个id暴力攻击。

从防止攻击的角度来说,也可以在网关层对ip设置访问阈值,超出阈值直接拉黑。

缓存击穿:

击穿可以认为是雪崩的一个特例。击穿不是大面积的缓存失效,而是热key失效,导致持续的大并发打到数据库,就像在redis中凿开了一个点。

解决方案:

设置热点数据不过期,或者加互斥锁

互斥锁:请求进来发现redis里没有数据,则直接加锁到数据库请求,然后塞入redis,解锁。加锁期间所有请求直接返回空。相当于一种降级方案。

上一篇下一篇

猜你喜欢

热点阅读