一、作用
用来判断海量数据中是否存在指定值。
二、原理
将容器中的所有值求hashcode然后根据hashcode找,类似于hashmap,但不同的是这里只比较hash值,没有hash值相同之后的进一步精确匹配。
所以出现hash碰撞时,不能确定是否真的存在,此时可以使用不同的hash算法对数组中元素设置多个值,然后对每个值进行匹配。所以只要算法够多,误判几率就越低。
三、应用场景
Hbase就使用布隆过滤器来找rowkey,也会有误判的情况,本来没有,但返回有,此时无非就是遍历一个hfile而已,白搜了一个文件。可以接受。
新老用户判断
爬虫
四、使用
1. Hbase使用的是guava中的api
BloomFilter也是相当于一个容器,只不过这个容器里会对放入的元素求多个hashcode,然后把这些hashcode作为这个元素的下标。所以使用BloomFilter时,先创建BloomFilter对象,然后把元素put进去。然后就可以使用mightContain()判断是否存在了。
scala代码中一定要注意,BloomFilter.create[String]要加泛型,不然会报trying to do lub/glb of typevar ?T
https://stackoverflow.com/questions/40585069/guava-bloomfilter-error-in-scala-compiler-trying-to-do-lub-glb-of-typevar-t
import com.google.common.base.Charsets
import com.google.common.hash.{BloomFilter, Funnels}
object 布隆过滤器 {
def main(args: Array[String]): Unit = {
// 样本
val arr: Array[String] = Array("asdf", "qw", "qwe", "qwer", "aa", "zxc", "zz", "bb")
// 创建bloomFilter,类型、可能的数据量(必须比实际数据量大,但一般都是实际量的10倍以上)、误判几率
val bloomFilter: BloomFilter[String] = BloomFilter.create[String](Funnels.stringFunnel(Charsets.UTF_8), 100000, 0.000001)
// 将样本中元素put到bloomFilter
arr.foreach(bloomFilter.put(_))
// 判断指定元素是否存在
println(bloomFilter.mightContain("qw"))
println(bloomFilter.mightContain("123"))
}
}
2. 使用hadoop中的布隆过滤器工具类,这个不需要额外导包,sparksql
的包中就有,core
没有
object hadoop布隆过滤器 {
def main(args: Array[String]): Unit = {
// 样本
val arr: Array[String] = Array("asdf", "qw", "qwe", "qwer", "aa", "zxc", "zz", "bb")
val bloomFilter = new BloomFilter(100000,4,1)
arr.foreach(ele => bloomFilter.add(new Key(ele.getBytes())))
println(bloomFilter.membershipTest(new Key("qw".getBytes())))
println(bloomFilter.membershipTest(new Key("12".getBytes())))
}
}
在spark中使用,有2个问题:
- 创建bloomFilter 时,需要获取所有样本的集合,而样本的数据是分布式的,所以要么collect到driver,要么把bloomFilter 作为累加器广播出去。
- 需要把bloomFilter 对象广播出去,而因为bloomFilter 是hadoop提供的,实现了writeable接口,没有实现serializable接口,MR可以序列化,spark不能。所以会报无法序列化的错。因为spark默认用jdk的序列化框架,而jdk序列化框架的标记就是serializable。此时,可以用spark的序列化框架kryo。当然也可以写个子类实现serializable。
==》
使用kryo,能自动将driver中的共享变量序列化