布隆过滤器
就是把数据通过hash函数计算出来标记在不同的位置
然后查找的时候也是用hash函数算出值,去找一下1,4,7上的数据是不是1,不是1的话就不存在
存入布隆过滤器的缺点就是不可以删除数据,但是与set不同在于可以加入重复的数据,因为其原理就是通过hash算法分散到不同的位置进行标记,重复只不过是重复了上述过程。
布隆过滤器使用场景
场景一:当前需要维护一个内存,去存储uid这个字段,可是这个字段数据量比较大可能有两百万左右,那么一个integer占用16个字节,hashset要16个字节,一共3200万字节
但是如果使用布隆过滤器可以有效的降低存储压力,其一个size就一个字节,两百万的数据就两百万字节,但是其有误判率,所以当我们将size扩大五倍,误判就大大减小了,也就一千万字节,也是原来的三分之一。
场景二:当大量的数据同时打到redis上,但redis上都没有数据,这个时候就可以用布隆过滤器,来过滤redis上是否有数据才去访问redis。
布隆过滤器的实现
手动实现(不推荐),因为已经有封装好的包了
import java.util.Arrays;
import java.util.BitSet;
public class MyBloomFilter {
//你的布隆过滤器容量
private static final int DEFAULT_SIZE = 2 << 28;
//bit数组,用来存放结果
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的扰动算法
private int hash(Object key, int i) {
int h;
return key == null ? 0 : (i * (DEFAULT_SIZE - 1) & ((h = key.hashCode()) ^ (h >>> 16)));
}
}
guava实现的布隆过滤器(推荐!!!)
//先加入maven
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
public class test {
private static int size = 200000000;
private static double fpp = 0.00001;
private static BloomFilter<Long> bloomFilter = BloomFilter.create(Funnels.longFunnel(), size, fpp);
public static void main(String[] args) {
bloomFilter.put(1l);
System.out.println(bloomFilter.mightContain(1l));
}
}
//其中Funnels.longFunnel()主要是数据类型,这里以long型举例,size就是布隆过滤器的大小,fpp是误判率,fpp越小运行的时间就越久
redis布隆过滤器
package com.ys.rediscluster.bloomfilter.redisson;
import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
public class RedissonBloomFilter {
public static void main(String[] args) {
Config config = new Config();
config.useSingleServer().setAddress("redis://192.168.14.104:6379");
config.useSingleServer().setPassword("123");
//构造Redisson
RedissonClient redisson = Redisson.create(config);
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("phoneList");
//初始化布隆过滤器:预计元素为100000000L,误差率为3%
bloomFilter.tryInit(100000000L,0.03);
//将号码10086插入到布隆过滤器中
bloomFilter.add("10086");
//判断下面号码是否在布隆过滤器中
System.out.println(bloomFilter.contains("123456"));//false
System.out.println(bloomFilter.contains("10086"));//true
}
}