什么是布隆过滤器?
背景:
官网解释:Low latency and compact probabilistic data structures(低延迟和紧凑的概率数据结构)
布隆过滤器(Bloom Filter)是一种用于判重的数据结构,其主要目的是在大规模数据集中高效地检查一个元素是否存在。布隆过滤器最早由Burton Howard Bloom于1970年提出,用于解决大规模数据判重问题。
应用场景:
- 避免缓存穿透
- 检查用户名是否重复
- 例如网络爬虫判重URL
- 解决推送过的文章、视频不再推送(抖音往下刷不再刷到相同视频)
- 邮件服务器判重邮件地址等。
传统的哈希表等数据结构在处理大规模数据时会占用大量的内存,而布隆过滤器通过使用位数组和多个哈希函数来解决这个问题。
优缺点:
优点:
- 占用内存少:相比于传统的哈希表,布隆过滤器使用位数组和哈希函数,占用的内存要少得多。
- 查询速度快:由于只需要计算多个哈希函数并查询位数组,布隆过滤器的查询速度非常快,判断时间复杂读O(N),(N是hash函数的个数)。
- 保密性好:布隆过滤器不存储数据本身
缺点:
- 误判率:由于多个元素可能映射到同一个位数组位置,因此存在一定的误判率,即判断一个元素不存在时,实际上可能存在。但可以通过调整位数组大小和使用更多的哈希函数来降低误判率。
- 很难删除元素(最好对长期存储、不轻易删除的数据使用布隆过滤器)【Cuckoo Filter可删除元素】
- 不能获取数据本身,只能用于判重
- 随着数据量的增加,误判率越来越高(可以理解为位数组为0的位置越来越少,发生hash冲突的概率越来越大)
工作原理:
布隆过滤器由一个位数组(Bit Array)和多个哈希函数组成。初始时,位数组的所有位都被置为0。当要添加一个元素时,会将这个元素经过多个哈希函数得到多个哈希值,然后将对应的位数组位置置为1。判断一个元素是否存在时,同样通过多个哈希函数计算出位数组位置,如果所有位置都是1,则说明可能存在,如果有任何一个位置为0,则说明该元素一定不存在。
- 对三个元素分别进行用3个hash函数计算其hash值,通过一个数组存储,比如:"123"用hash1求值为0,则将
arr[0]设置为1 - 在判断的时候只需用hash函数对带判断元素求值value,若存在一个
arr[value]==0则此元素不存在,如果都为1则存在 - 因为hash函数会产生hash冲突(两个不相同的元素通过hash函数的计算得出的值是一样的),所以布隆过滤器所判断的存在不一定存在,也可能是发生了hash冲突
布隆过滤器说不存在,就一定不存在;说存在不一定存在
怎么用JAVA操作布隆过滤器?
redis能够集成布隆过滤器,但是过程比较麻烦
-
笔者使用的是Linux虚拟机(CentOS7),安装了redis 6.0.5,然后用主机:window去连接
-
在虚拟机中,去官网下载了RedisBloom-2.2.18,解压并编译
-
配置redis.config文件,
- 设置
protected-mode为no(是否开启保护模式。yes只允许本地访问,no可以远程访问) - 设置
daemonize为yes(是否后台运行,yes为是,no为否) bind 127.0.0.0改成bind 0.0.0.0(只允许本机的连接请求。修改后可以接受任意请求)- 添加RedisBloom编译后的os文件位置(我的是:loadmodule /usr/local/redis/RedisBloom-2.2.18/redisbloom.so)
- 然后是修改防火墙,打开redis的服务器端口(我的是:6379)
- 设置
-
使用redisson操作布隆过滤器
-
引入
pom.xml依赖<dependencies> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.13.6</version> </dependency> </dependencies> -
java代码:
-
先创建
configConfig config = new Config(); config.useClusterServers() .addNodeAddress("redis://ip地址:端口号"); -
创建客户端
RedissonClient redisson = Redisson.create(config); -
创建一个存储Long类型的布隆过滤器,并初始化
RBloomFilter<Long> numFilter = redisson.getBloomFilter("numFilter"); numFilter.tryInit(位数组长度, 误判率);//误判率值为0~1之间的小数 -
增加元素并打印布隆过滤器元素个数(往往不等于n值)
for (long i = 0; i < n; i++) { System.out.println("添加了"+i); numFilter.add(i); } long elementCount = numFilter.count(); System.out.println("elementCount ="+elementCount); -
计算误判次数(用n个存在数字进行判断),输出值大都能匹配上之前设置的误判率
int count = 0; for (long i = n; i < n * 2; i++) { if (numFilter.contains(i)) { System.out.println("误判:"+i); count++; } } System.out.println("误判次数 ="+count);
-
删除这个布隆过滤器,并关闭连接
numFilter.delete(); redisson.shutdown();
-