0 什么是布隆过滤器
布隆过滤器(Bloom Filter) 是由巴顿布隆于1970提出,是一种通过多个哈希函数映射来对参数存储空间进行压缩的数据结构;本质上是由一个比较长的二进制位向量和一组hash函数映射构成。 由于布隆过滤器不需要存储元素本身,节省了大量的空间,所以其在数据查重时具有很大优势。
布隆过滤器的优点是空间效率和查询时间都远远超过⼀一般的算法,缺点是有一定的误识别率和删除困难,因此其不适合对误判率要求较高的场景。有研究表明如果想保持较低的误判率,布隆过滤器的使用空间应低于50%。
1 布隆过滤器实现原理
假设某个布隆过滤器的位向量大小为m,且在初始状态每一位都是0,如下图所示。
现有n个元素数据集合{x1,x2,x3…xn},布隆过滤器通过k个hash函数将集合中的每一个元素映射到布隆过滤器的k个位置上,并将相应向量位置为1。如果置位时发现该位值已经是1了,可以不进行操作。如下图所示x1和x2 通过3个hash函数的映射过程。
2 布隆过滤器应用场景
根据布隆过滤器优缺点,布隆过滤过滤器同城使用来检索某个元素是否在存在。典型如 爬虫中url去重,redis缓存穿透场景。比如在缓存穿透的场景中,可以在把数据写入数据库时,使用布隆过滤器做个标记。当缓存缺失时候,可以通过查询布隆过滤器来快速判断数据是否存在,如果不存在,就不用在去查询数据库了,这样一来即使发生缓存穿透,也不会有大量的流量打到DB。
3 实践
自己编写一个简单的布隆过滤器,代码如下。
/**
* @author hsc
* @date 2021/4/3
*/
public class MyBloomFilter {
private final static int DEFAULT_SIZE = 1024 * 1024;
private int size;
private BitSet bitSet;
private List<HashFunction> hashFunctions = new ArrayList<>(8);
public MyBloomFilter() {
//这里面默认使用3个hash函数
for (int i = 0; i < 3; i++) {
hashFunctions.add(new HashFunction(i + 2));
}
bitSet = new BitSet(DEFAULT_SIZE);
size = DEFAULT_SIZE;
}
private int getBitIndex(int hash) {
return (size - 1) & hash;
}
public void add(String value) {
if (value == null) {
return;
}
for (HashFunction f : hashFunctions) {
bitSet.set(getBitIndex(f.hash(value)), true);
}
}
public boolean containValue(String value) {
if (value == null) {
return false;
}
for (HashFunction f : hashFunctions) {
boolean result = bitSet.get(getBitIndex(f.hash(value)));
if (!result) {
return false;
}
}
return true;
}
private static class HashFunction {
private int seed;
public HashFunction(int seed) {
this.seed = seed;
}
public int hash(String value) {
int result = 0;
int len = value.length();
for (int i = 0; i < len; i++) {
result = seed * result + value.charAt(i);
}
return Math.abs(result);
}
}
public static void main(String[] args) {
MyBloomFilter bloomFilter = new MyBloomFilter();
for (int i = 0; i < 1000000; i++) {
bloomFilter.add(String.valueOf(i));
}
System.out.println(bloomFilter.containValue("121212"));
}
}
Google Guava 中已经包含BloomFilter实现,我们可以直接使用。
BloomFilter bloomFilter=BloomFilter.create(Funnels.stringFunnel(Charset.forName("UTF-8")),10000,0.01);
for (int i = 0; i < 1000000; i++) {
bloomFilter.put(String.valueOf(i));
}
System.out.println(bloomFilter.mightContain("121212"));
在redis中 布隆过滤器作为一个插件加载到 Redis Server 中,给 Redis 提供了强大的分布式去重功能。在已安装 Redis 的前提下, 需要安装RedisBloom插件。