布隆过滤器
了解
- 1970年,由Bloom 提出的
- 由二进向量(位数组)与 随机映射函数组成映射关系
- BitSet 作为位容器(位数组),位值为 0 或者 1
- 随机映射函数(HashFunction),目的生成一个对应的数值,然后存入到 BitSet中。
- 优点:
- 数组容器占用空间小。存储100W个数据,占 122 KB(100W*1bit/8/1024)
- 如果存储过数据,容器必定有痕迹
- 缺点:
- 容器有痕迹的,不一定存储过数据
- 容器中只能添加数据,不能删除数据。删除元素导致位值的变化,会提高容器的错误率
- 数据量越大,错误率会越高。所以一般 位容器的初始位数会很大
注 : 1 M = 1024 KB = 1024 * 1024 B(byte) = 1024 * 1024 * 8 bit
原理
添加对象
被添加的对象,通过 HashFunction 会生产一个或多个对应的数值,并存储到 BitSet 容器中。BitSet 中位值会由 0->1 ,多个对象存储时,位值也只会存储到 1。
判断对象
判断的过程中,实际是看对象通过 HashFunction 所生产的一个或多个对应的数值,在 BitSet 中的位值是否都为1。
图中,对象4 对应的所有位值里面,存在一个值为 0,则 对象4 一定没存储过。
实现
/**
* 布隆过滤器
*
* @author ZZ_C
*
*/
public class TestBloomFilter {
/**
* 一个长度为10亿的位
*/
private static int DEFAULT_SIZE = 256 << 22;
/**
* 为了降低错误率,使用hash算法,定义8个不同的质数
*/
private static final int[] seeds = { 3, 5, 7, 11, 13, 17, 19, 23 };
/**
* 定义seeds.length 个 hash 函数用于哈希计算
*/
private static HashFunction[] functions = new HashFunction[seeds.length];
/**
* 布隆过滤器的核心容器,初始化,定义了 10 亿个位
*/
private static BitSet bitSet = new BitSet(DEFAULT_SIZE);
TestBloomFilter() {
for (int i = 0; i < seeds.length; i++) {
functions[i] = new HashFunction(DEFAULT_SIZE, seeds[i]);
}
}
public void add(String value) {
for (HashFunction function : functions) {
bitSet.set(function.toHash(value));
}
}
public boolean contains(String value) {
for (HashFunction function : functions) {
// 只要有一个不存在,则 value 一定不存在
boolean result = bitSet.get(function.toHash(value));
if (Objects.equals(result, false)) {
return false;
}
}
return true;
}
public static void main(String[] args) {
TestBloomFilter bloomFilter = new TestBloomFilter();
bloomFilter.add("斗罗大陆");
bloomFilter.add("辰东");
System.out.println(bloomFilter.contains("我爱吃西红柿")); // false
System.out.println(bloomFilter.contains("辰东")); // true
}
/**
* 作用就是算 hash值
* 静态类,便于处理
* @author ZZ_C
*
*/
static class HashFunction {
private int size;
private int seed;
public HashFunction(int size, int seed) {
super();
this.size = size;
this.seed = seed;
}
public int toHash(String value) {
int h;
return (value == null) ? 0 : Math.abs(seed * (size - 1) & ((h = value.hashCode()) ^ (h >>> 16)));
}
}
}
应用场景
Bloom Filter 在使用场景上分为两种,一种是单体架构,一个是分布式架构。
单体架构:Gava,redis
分布式架构: redis bloom(需要自行安装到 redis 中)
缓存击穿
使用布隆过滤器,将全部的有效数据都走一遍布隆过滤器,这样新来请求时,先走一遍请求的元素是否能通过布隆过滤器的验证,若未通过就直接放弃请求,若通过再执行查询
去重
用户浏览记录存入数据库时,会在Filter上通过key的hash算法存储判断其是否存在,类似于数据存在数据库中,判断该数据是否存在的信息即元数据存放在BloomFilter中,避免了每次判断数据是否存在都要去数据库exist一遍;这样推送新闻时通过布隆过滤器判断,推送内容是否已经存在,如果存在则不推送,如果不存在则推送;