背景
公司用户中心,有大量的用户请求,为防止缓存击穿,需要设计一个缓存策略 ,将恶意请求过滤掉。
什么是缓存击穿得搞清楚。就是有人恶意传入本来数据库就不存在的用户ID,然后大量请求数据库导致数据库挂掉(这里的缓存使用redis)
需求设计
-
redis没有拦住,那肯定是没有拦住的呀。因为redis中没有这个数据,一般策略就是去数据库中读,如果读到了就返回且更新到缓存中(且设置一个过期时间),没有读到就进行回填空值到reids中防止同一个数据库中不存在userId多次请求。但是这里的userId是不同的且不存在。如果是这样的话每来一个直接回填一个那他妈短时间内岂不是对于redis也有压力了。
在这里插入图片描述 - 下面我们对布隆过滤器进行简单的介绍
布隆过滤器
简介
- 可以用来存储不重复的数据,不是真实数据,只是一个数据标识(hash function 计算出来的值),且可以占用少量的空间,且能达到较高的查询性能,但是会在有小的误差,当布隆过滤器说某个值存在时,这个值一定存在;当它说不存在时,那就可能不存在。在对于布隆过滤器见过的值不会出现误差。
- 那我们换个思路就是把数据库的数据ID同步一份到缓存中 如果有再请求reids拿key对应的value,如果redis没有再去数据库。如果说这个缓存中的valua中没有这个userId那我们就是直接返回。
数据结构
- 底层数据结构是一个具有固定大小长度的M bit array。布隆过滤器每存储一个值通过计算n次 hash function 将对应的数组位置从0置为1。当进行再次add的时候寻找对应的位置如果任意一位都不为1,就认为这个数据是不重复的。
-
上面也说了布隆过滤器是不允许删除的元素的。由于你删除的元素及其有可能和其他数据元素公用同一个slot。当你删除了这个元素,那你其他元素也就乱掉了。如下图所示 一个key对应3个slot。你删除调key1 也就是将 F,G,H进行恢复为0,那key2毫无疑问就挂掉了。
在这里插入图片描述 - 那我们进行contains比较的时候时间复杂度和空间复杂度是多少呢? 由于是一个连续数组,每次定位一个slot那就是O(1),如果有k个hash func ion那时间复杂度就是O(k)也算是常数级别的。
那他插入和查找的复杂度就是O(K); - 和set对比一下 set的定位是否重复也是O(1),但是他的战用空间是很大的,等于说是一个hash对应一个位置也就是说这个hash的散列很牛逼(在这里可以联想一下hashMap中将key进行hash计算的列子)。也就是hash的这个值是不能重复使用空间的。 通过专家的计算大概布隆过滤器可以大约90%的空间。主要一部分空间差距在 set会存储值,er布隆过滤器只需要存储值的指纹(指的就是多个hash function 计算出来的内存位置既数组下标位置 )。
- 布隆过滤器的误差率是有迹可循的, 可以根据预估的存入元素的数量来得出自己满意的错误率
布隆过滤器有两个参数,第一个是预计元素的数量 n,第二个是错误率 f。公式根据这
两个输入得到两个输出,第一个输出是位数组的长度 l,也就是需要的存储空间大小 (bit),
第二个输出是 hash 函数的最佳数量 k。hash 函数的数量也会直接影响到错误率,最佳的数
量会有最低的错误率。
k=0.7*(l/n) # 约等于
f=0.6185^(l/n) # ^ 表示次方计算,也就是 math.pow
12
- 说了这么多,这在代码中自己写个布隆过滤器,也不敢用啊。布隆过滤器这么牛逼。那有没有那种直接可以用的呢?
redis4.0 直接上手布隆过滤器
- 在redis 4.0以后就有了布隆过滤器的插件。可直接使用jedis bf.add bf.exist bf.madd bf.mexist 进行插入和查找,现在的redisson是真的太强大了。什么都有redisson布隆过滤器的使用
- 在使用redis插件布隆过滤器的时候我们也可以进行指定存储元素的大小和错误率,通过bf.reserve 进行创建,但是如果已经有key存在就会出现err
有三个参数 key, error_rate 和 initial_size。错误率越低,需要的空间越大。initial_size 参数表示预计放
入的元素数量,当实际数量超出这个数值时,误判率会上升。 所以在初始设置是一定要大概率准确。
总结
- 设计了简单的使用布隆过滤器拦截恶意请求
- 布隆过滤器是一个概率型数据结构,他可以存储不同数据的指纹。可以通过O(1)复杂度进行查找和添加,存储空间比set集合少百分之90.但是会对没见过的数据判断出现误差(出现误差的原因是一也就是hash碰撞)。
- 还有redis4.0 对布隆过滤器的支持以及redisson
网友评论