Redis缓存穿透的解决方案

这是我参与更文挑战的第7天,活动详情查看: 更文挑战

前言

在我们系统访问量不高的时候,通常我们的架构很简单,服务接收到请求,直接往数据库上怼进去就可以了。

image.png
但是随着用的人数越来越多,并发访问量越来越高,我们通常会考虑使用redis作为数据缓存来提升系统性能。

image.png

什么是缓存穿透

如果有人恶意攻击你的系统,用一个redis和mysql中都不存在的数据去大量的请求你的服务,那么这些请求相当于穿透了redis服务,又会全部打到数据库上去。如果高频请求,在达到mysql瓶颈的时候就会造成宕机等服务不可用的问题。

怎么解决缓存穿透问题

1. 缓存空值。

面对访问redis和mysql都查不到对应记录的key(比如id = -1),我们可以将空值也写入到缓存中,那么下次相同的请求再进来的时候,redis中就可以找到这条记录,redis就可以帮我们抵挡这些无效的请求,防止这些请求进入数据库。

看上去完美地解决了我们的问题,但是这种做法其实有个弊端。如果这种恶意攻击每次带的key都是不同的情况,比如用UUID。这时候我们的缓存中就会缓存到大量的无用数据,redis的数据是保存在我们的内存中的,我们知道redis不可能让数据无限的膨胀下去的,它是有内存淘汰策略的。在内存占用达到指定值,会根据设定的淘汰策略,删除掉即将过期的或者是最少被使用的数据。

使用这种方法解决,可能会导致redis缓存的都是无效的数据,导致我们正常的数据都被删除掉,结果可能适得其反。

2. 布隆过滤器
第二种方法就是在redis和数据库中间再加一层过滤器,如果过滤器发现不存在该数据,直接返回无效请求。

image.png

关于布隆过滤器你可以直接简单粗暴地理解为:它说没有的肯定没有,它说有的也可能没有。

布隆过滤器是由一个bitSet和三个hash函数组成的。bit占用的空间很小,所以在布隆过滤器中可以存储非常多的数据。

它的hash函数只需要满足尽可能的散列(必然会存在hash碰撞问题,这也是它有一定错误率的原因。),以及hash值要在数组长度之间。

在初始化时每个bit都是0,如果存在数据的hash值在这个下标位置上,那么这个下标的值就改为1.

image.png

我们上面说到布隆过滤器会存在一定错误率,那么如何减小这个错误率呢?

  • 我们可以加大数组的长度,这样出现冲突的概率就会降低
  • 还有就是我们可以用不止三个hash函数,越多次hash,每个值对应的bit也就越多,也就越不容易出现误判。

这里多说一句,正常新增数据,我们不推荐立刻往布隆过滤器中写入,可以写一个定时任务去定时异步写入,这样系统的性能会更好。

以上,感谢阅读。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享