布隆过滤器的由来
布隆过滤器为啥不叫艾希过滤器?不叫丽桑卓过滤器?
因为它是1970年由一个叫布隆的大佬设计的,就像如果是我设计的,那就叫番茄过滤器。
布隆过滤器简介
布隆过滤器实际上是一个很长的二进制向量(可以理解为就是个数组)和一系列随机映射函数(hash函数)。布隆过滤器可以用于检索一个元素是否存在。它的优点是空间效率和查询时间都比一般的算法要好的多,并且占用空间小,缺点是有一定的误识别率和删除困难。一般用于过滤请求的非法key(比如查询年龄为-1的人),缓解缓存的击穿问题。
布隆过滤器能解决啥问题
明人不说暗话,这玩意就是用来解决缓存穿透的,就是当有些数据库中不存在的数据被用户请求访问,一开始去redis里查询,肯定是查不到,数据库里都没有缓存里咋可能有,这时这些请求就会直接进入数据库中,数据库中也没有,于是本次查询失败,这种情况一次两次还行,次数多了数据库遭不住,布隆过滤器就是用来缓解这个情况的。
布隆过滤器工作原理
布隆过滤器会把每个已有的数据进行若干个不同的hash运算(可以自定义)得到一个key,然后根据这个key去修改自己数组里的对应下标的值(从0改成1),查询的时候直接把请求中的key进行hash运算后去查看数组中对应下标的值是否为1,如果为1,则说明这个数据可能存在,如果出现了0,那这个数据就肯定不存在,为什么?
看我举一个栗子
例:
现在有两个元素a和b,通过计算得出
a的key为[0,3,5]
b的key为[3,5]
布隆过滤器的初始数组为[0,0,0,0,0,0,0,0]
此时数据库中写入a元素,布隆过滤器会对其进行记录
记录后的数组为[1,0,0,1,0,1,0,0,0]
这时进来一个请求要查询b,b的key通过运算得出为3和5,在布隆过滤器中查询结果全为1,但是数据库中是没有b这个数据的,这就是为啥即使全为1数据也不一定存在的理由。虽然布隆过滤器存在误判率,但是肯定利大于弊的(总比被穿透好撒)。
贴个Demo(不是我写的)
代码来自一位不愿意透露姓名的靓仔->靓仔的主页。
如果对hash算法有啥疑惑的点这->帮你真正理解hashCode和hash算法
public class MyBloomFilter {
//你的布隆过滤器容量
private static final int DEFAULT_SIZE = 2 << 28;
//bit数组,用来存放key
private static BitSet bitSet = new BitSet(DEFAULT_SIZE);
//后面hash函数会用到,用来生成不同的hash值,可随意设置,别问我为什么这么多8,图个吉利
private static final int[] ints = {1, 6, 16, 38, 58, 68};
//add方法,计算出key的hash值,并将对应下标置为true
public void add(Object key) {
Arrays.stream(ints).forEach(i -> bitSet.set(hash(key, i)));
}
//判断key是否存在,true不一定说明key存在,但是false一定说明不存在
public boolean isContain(Object key) {
boolean result = true;
for (int i : ints) {
//短路与,只要有一个bit位为false,则返回false
result = result && bitSet.get(hash(key, i));
}
return result;
}
//hash函数,借鉴了hashmap的扰动算法,强烈建议大家把这个hash算法看懂,这个设计真的牛皮加闪电
private int hash(Object key, int i) {
int h;
return key == null ? 0 : (i * (DEFAULT_SIZE - 1) & ((h = key.hashCode()) ^ (h >>> 16)));
}
}
测试
public static void main(String[] args) {
MyNewBloomFilter myNewBloomFilter = new MyNewBloomFilter();
myNewBloomFilter.add("张学友");
myNewBloomFilter.add("郭德纲");
myNewBloomFilter.add("蔡徐鸡");
myNewBloomFilter.add(666);
System.out.println(myNewBloomFilter.isContain("张学友"));//true
System.out.println(myNewBloomFilter.isContain("张学友 "));//false
System.out.println(myNewBloomFilter.isContain("张学友1"));//false
System.out.println(myNewBloomFilter.isContain("郭德纲"));//true
System.out.println(myNewBloomFilter.isContain("蔡徐老母鸡"));//false
System.out.println(myNewBloomFilter.isContain(666));//true
System.out.println(myNewBloomFilter.isContain(888));//false
}
插一嘴
这里介绍一下布隆过滤器的升级版(升了但只能升一点点)——布谷鸟过滤器
布谷鸟过滤器有两张hash表,两个表的hash函数也不一样,当有数据进来后,会分别算出两个表中对应的位置,然后尝试放入其中一个表,如果表中对应位置为空,则放入成功,如果该表位置已经有数据抢占了,就去尝试放入另一张表。当然,最后肯定还是会出现问题,当一个数据两张表的位置都被占了,这代表布谷鸟过滤器的hash算法达到了性能瓶颈,此时需要对哈希算法进行性能优化。总的来说布谷鸟过滤器在错误率小于3%的时候空间性能是优于布隆过滤器的,而这个条件在实际应用中常常满足,所以一般来说它的空间性能是要优于布隆过滤器的。
内容参考:布谷鸟过滤器。
PS:所有的内容都是我自己的理解,肯定有不对的地方,欢迎指正。