1.定义
BloomFilter 是由一个固定大小的二进制向量或者位图(bitmap)和一系列映射函数组成的。用来解决一个元素是否存在一个集合里面(一般是很大的集合里),是一种算法。
2.场景
- 用户名是否注册过
- 缓存穿透:缓存和数据库中都没有的数据,导致每次都要重新查询,而用户恶意不断发起请求,导致一直查询数据库导致资源浪费。
- 网页爬虫判断是否爬过URL的判断
- 反垃圾邮件,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱;
2.1传统的解决方案:
- 1.使用数据库存储用户数据,没有都查询用户是否在数据库里。如果超过1亿条,速度会超慢。
- 3.4. 如果使用Set对象存储对应的查询数据如 url或邮件地址,当数据量超过几个G,内存就会被溢出。
2.2使用布隆过滤器的方案:
每次写入用户数据都更新一下 这个长长的二进制的位图,当有请求的时候,先在这个二进制位图查询。如果有才进行数据库查询。
3.原理
一个集合C里面的有n元素,把n个元素通过k个哈希函数,分别转化映射到二进制向量里面。这些位置都标识为1,当要校验某个x元素是否在集合里,只需要把x元素进行同样的哈希函数转化,查看标识1的位置 与二进制向量表是否匹配,如果相同位置同时也是1 则存在。
4.例子🌰
假设有集合{'a','f','h'},需要检查'h' 是否在集合中
-
刚开始二进制向量 所有位置都是0
-
'a'经过hash1哈希之后,在第二个位置标识为1
-
'f'经过hash1哈希之后,在第4个位置标识为1
-
'h'经过hash1哈希之后,在第6个位置标识为1
这时候{'a','f','h'}映射的二进制向量已经映射好,校验'h'是否存在,只需要执行hash1('h'), 然后检查对应的位置是否已经标识为1即可。是1则存在,是0则不存在。
5.总结
2.2.1优点
通过一个很长的二进制向量就能描述各种复杂的数据关系,空间复杂度极低。查询时间也比较快。
2.2.2特点
有则有可能有误差,有哈希碰撞的可能,但是没有就肯定没有。哈希函数可以有效均匀的分布在整个位图里,减少哈希碰撞。
2.2.2缺点
由于有可能有误差,有哈希碰撞的可能,导致结果不精确。删除也不方便。
优化方案:可以增加hash哈希函数的数量与位图的长度。
6.推荐第三方库
Google的Guava类,Twitter的 Algebird 类库,Redis自带的Bitmaps二次开发。