还是总结了一下,网上的帖子太长不想看
8.布隆过滤器
定义:一种数据结构,是由一串很长的二进制向量组成(二进制数组),初始默认值都是0
添加
添加一个元素key时,通过多个hash函数算出一个值,然后将这个值所在的方格置为1
查找
将这个新的数据通过上面自定义的几个哈希函数分别算出各个值,然后看其对应的地方是否都是1,如存在一个不是1的,那该数据一定不存在这个布隆过滤器中
布隆过滤器优缺点
优点:二进制组成的数组,占用内存少,且插入和查询速度都快(支持海量数据场景下高效判断元素是否存在)
缺点:随着数据增加 误判率增加;无法判断数据一定存在;无法删除数据
使用
UUID.randomUUID().toString()生成的随机数是不重复的,所以下面可以用来看重复率 @Test public void testOss() { BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), 5000000, 0.01); List<String> list = new ArrayList<>(5000000); for (int i = 0; i < 5000000; i++) { String uuid = UUID.randomUUID().toString(); bloomFilter.put(uuid); list.add(uuid); } NumberFormat percentFormat = NumberFormat.getPercentInstance(); percentFormat.setMaximumFractionDigits(2); int mightContainNumber2 = 0; for (int i = 0; i < 5000000; i++) { String key = UUID.randomUUID().toString(); if (bloomFilter.mightContain(key)) { mightContainNumber2++; //看这50000次生成的随机数,在之前生成后放入布隆过滤器中有多少重复,有的就是误判的 } } System.out.println("【key不存在的情况】布隆过滤器认为存在的key值数:" + mightContainNumber2); System.out.println("【key不存在的情况】布隆过滤器的误判率为:" + percentFormat.format((float) mightContainNumber2 / 5000000)); } 【key不存在的情况】布隆过滤器认为存在的key值数:50256 【key不存在的情况】布隆过滤器的误判率为:1.01%
使用场景
1.缓存击穿场景,如查数据,先查是否在redis缓存中,存在则直接返回,不存在则查mysql数据库。如果来一波冷数据,会导致缓存大量击穿(全部跳过redis去查数据库),造成雪崩效应
这时候可以用布隆过滤器当缓存的索引,只有在布隆过滤器中,才去查询redis缓存,如果没查询到则穿透到数据库查询。如果不在布隆过滤器中,则直接不查返回 2.网页爬虫对URL去重,采用布隆过滤器来对已经爬取过的URL进行存储,这样在进行下一次爬取的时候就可以判断出这个URL是否爬取过了
3.判断用户是否阅读过某一个视频或者文章,刷过的视频往下滑动不再刷到
4.WEB拦截器,相同请求则拦截,防止被攻击。第一次请求,将请求参数放入布隆过滤器中,第二次请求,先判断请求参数是否被布隆过滤器命中
注意
1.布隆过滤器可以判断某个数据一定不存在,但是无法判断一定存在
无法判断一定存在的原因:
情况1:想hash存储的数据经过 几个映射函数运算得到的三个点跟 已经hash运算存储数据的点位 是一样的,出现误判 情况2:想hash存储的数据 经过运算得到三个点位上的 1 是两个不同的数据经过运算后得到的
2.不存储数据本身,仅存储hash结果取模运算后的位标记
3.其中hash函数的个数会随规定的误差而变化,规定期望的误差越小,hash函数个数越多,过滤器越长