Java技术栈

www.javastack.cn

打开网站看更多优质文章

作者:CodeBear的园子

www.cnblogs.com/CodeBear/p/10911177.html

本文是站在小白的角度去讨论布隆过滤器,如果你是科班出身,或者比较聪明,又或者真正想完全搞懂布隆过滤器的可以移步。

不知道从什么时候开始,本来默默无闻的布隆过滤器一下子名声大燥,仿佛身在互联网,做着开发的,无人不知,无人不晓,哪怕对技术不是很关心的小伙伴也听过它的名号。

我也花了不少时间去研究布隆过滤器,看了不少博客,无奈不是科班出身,又没有那么聪明的头脑,又比较懒...经过“放弃,拿起,放弃,拿起”的无限轮回,应该算是了解了布隆过滤器的核心思想,所以想给大家分享下。

布隆过滤器的应用

我们先来看下布隆过滤器的应用场景,让大家知道神奇的布隆过滤器到底能做什么。

缓存穿透

我们经常会把一部分数据放在Redis等缓存,比如产品详情。这样有查询请求进来,我们可以根据产品Id直接去缓存中取数据,而不用读取数据库,这是提升性能最简单,最普遍,也是最有效的做法。面试常问,缓存三大问题及解决方案!一般的查询请求流程是这样的:先查缓存,有缓存的话直接返回,如果缓存中没有,再去数据库查询,然后再把数据库取出来的数据放入缓存,一切看起来很美好。但是如果现在有大量请求进来,而且都在请求一个不存在的产品Id,会发生什么?既然产品Id都不存在,那么肯定没有缓存,没有缓存,那么大量的请求都怼到数据库,数据库的压力一下子就上来了,还有可能把数据库打死。虽然有很多办法都可以解决这问题,但是我们的主角是“布隆过滤器”,没错,“布隆过滤器”就可以解决(缓解)缓存穿透问题。至于为什么说是“缓解”,看下去你就明白了。

大量数据,判断给定的是否在其中

现在有大量的数据,而这些数据的大小已经远远超出了服务器的内存,现在再给你一个数据,如何判断给你的数据在不在其中。

如果服务器的内存足够大,那么用HashMap是一个不错的解决方案,理论上的时间复杂度可以达到O(1),但是现在数据的大小已经远远超出了服务器的内存,所以无法使用HashMap,这个时候就可以使用“布隆过滤器”来解决这个问题。但是还是同样的,会有一定的“误判率”。

什么是布隆过滤器

布隆过滤器是一个叫“布隆”的人提出的,它本身是一个很长的二进制向量,既然是二进制的向量,那么显而易见的,存放的不是0,就是1。

现在我们新建一个长度为16的布隆过滤器,默认值都是0,就像下面这样:

现在需要添加一个数据:

我们通过某种计算方式,比如Hash1,计算出了Hash1(数据)=5,我们就把下标为5的格子改成1,就像下面这样:

我们又通过某种计算方式,比如Hash2,计算出了Hash2(数据)=9,我们就把下标为9的格子改成1,就像下面这样:

还是通过某种计算方式,比如Hash3,计算出了Hash3(数据)=2,我们就把下标为2的格子改成1,就像下面这样:

这样,刚才添加的数据就占据了布隆过滤器“5”,“9”,“2”三个格子。

可以看出,仅仅从布隆过滤器本身而言,根本没有存放完整的数据,只是运用一系列随机映射函数计算出位置,然后填充二进制向量。

这有什么用呢?比如现在再给你一个数据,你要判断这个数据是否重复,你怎么做?

你只需利用上面的三种固定的计算方式,计算出这个数据占据哪些格子,然后看看这些格子里面放置的是否都是1,如果有一个格子不为1,那么就代表这个数字不在其中。

这很好理解吧,比如现在又给你了刚才你添加进去的数据,你通过三种固定的计算方式,算出的结果肯定和上面的是一模一样的,也是占据了布隆过滤器“5”,“9”,“2”三个格子。

但是有一个问题需要注意,如果这些格子里面放置的都是1,不一定代表给定的数据一定重复,也许其他数据经过三种固定的计算方式算出来的结果也是相同的。这也很好理解吧,比如我们需要判断对象是否相等,是不可以仅仅判断他们的哈希值是否相等的。

也就是说布隆过滤器只能判断数据是否一定不存在,而无法判断数据是否一定存在。

按理来说,介绍完了新增、查询的流程,就要介绍删除的流程了,但是很遗憾的是布隆过滤器是很难做到删除数据的,为什么?你想想,比如你要删除刚才给你的数据,你把“5”,“9”,“2”三个格子都改成了0,但是可能其他的数据也映射到了“5”,“9”,“2”三个格子啊,这不就乱套了吗?

相信经过我这么一介绍,大家对布隆过滤器应该有一个浅显的认识了,至少你应该清楚布隆过滤器的优缺点了:

  • 优点:由于存放的不是完整的数据,所以占用的内存很少,而且新增,查询速度够快;

  • 缺点:随着数据的增加,误判率随之增加;无法做到删除数据;只能判断数据是否一定不存在,而无法判断数据是否一定存在。

可以看到,布隆过滤器的优点和缺点一样明显。

在上文中,我举的例子二进制向量长度为16,由三个随机映射函数计算位置,在实际开发中,如果你要添加大量的数据,仅仅16位是远远不够的,为了让误判率降低,我们还可以用更多的随机映射函数、更长的二进制向量去计算位置。

guava实现布隆过滤器

现在相信你对布隆过滤器应该有一个比较感性的认识了,布隆过滤器核心思想其实并不难,难的在于如何设计随机映射函数,到底映射几次,二进制向量的长度设置为多少比较好,这可能就不是一般的开发可以驾驭的了。

好在Google大佬给我们提供了开箱即用的组件,来帮助我们实现布隆过滤器,现在就让我们看看怎么Google大佬送给我们的“礼物”吧。

首先在pom引入“礼物”:

<dependency>  <groupId>com.google.guavagroupId>  <artifactId>guavaartifactId>  <version>19.0version>dependency>

然后就可以测试啦:

private static int size = 1000000;//预计要插入多少数据private static double fpp = 0.01;//期望的误判率private static BloomFilter bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, fpp);public static void main(String[] args) {//插入数据for (int i = 0; i < 1000000; i++) {    bloomFilter.put(i);  }int count = 0;for (int i = 1000000; i < 2000000; i++) {if (bloomFilter.mightContain(i)) {      count++;      System.out.println(i + "误判了");    }  }  System.out.println("总共的误判数:" + count);}

代码简单分析:

我们定义了一个布隆过滤器,有两个重要的参数,分别是 我们预计要插入多少数据,我们所期望的误判率,误判率不能为0。

我向布隆过滤器插入了0-1000000,然后用1000000-2000000来测试误判率。

运行结果:

1999501误判了1999567误判了1999640误判了1999697误判了1999827误判了1999942误判了总共的误判数:10314

现在总共有100万数据是不存在的,误判了10314次,我们计算下误判率:

和我们定义的期望误判率0.01相差无几。

redis实现布隆过滤器

上面使用guava实现布隆过滤器是把数据放在本地内存中,无法实现布隆过滤器的共享,我们还可以把数据放在redis中,用 redis来实现布隆过滤器,我们要使用的数据结构是bitmap,你可能会有疑问,redis支持五种数据结构:String,List,Hash,Set,ZSet,没有bitmap呀。没错,实际上bitmap的本质还是String。

可能有小伙伴会说,纳尼,布隆过滤器还没介绍完,怎么又出来一个bitmap,没事,你可以把bitmap就理解为一个二进制向量。

要用redis来实现布隆过滤器,我们需要自己设计映射函数,自己度量二进制向量的长度,这对我来说,无疑是一个不可能完成的任务,只能借助搜索引擎,下面直接放出代码把。

public class RedisMain {static final int expectedInsertions = 100;//要插入多少数据static final double fpp = 0.01;//期望的误判率//bit数组长度private static long numBits;//hash函数数量private static int numHashFunctions;static {        numBits = optimalNumOfBits(expectedInsertions, fpp);        numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits);    }public static void main(String[] args) {        Jedis jedis = new Jedis("192.168.0.109", 6379);for (int i = 0; i < 100; i++) {long[] indexs = getIndexs(String.valueOf(i));for (long index : indexs) {                jedis.setbit("codebear:bloom", index, true);            }        }for (int i = 0; i < 100; i++) {long[] indexs = getIndexs(String.valueOf(i));for (long index : indexs) {                Boolean isContain = jedis.getbit("codebear:bloom", index);if (!isContain) {                    System.out.println(i + "肯定没有重复");                }            }            System.out.println(i + "可能重复");        }    }/**     * 根据key获取bitmap下标     */private static long[] getIndexs(String key) {long hash1 = hash(key);long hash2 = hash1 >>> 16;long[] result = new long[numHashFunctions];for (int i = 0; i < numHashFunctions; i++) {long combinedHash = hash1 + i * hash2;if (combinedHash < 0) {                combinedHash = ~combinedHash;            }            result[i] = combinedHash % numBits;        }return result;    }private static long hash(String key) {        Charset charset = Charset.forName("UTF-8");return Hashing.murmur3_128().hashObject(key, Funnels.stringFunnel(charset)).asLong();    }//计算hash函数个数private static int optimalNumOfHashFunctions(long n, long m) {return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));    }//计算bit数组长度private static long optimalNumOfBits(long n, double p) {if (p == 0) {            p = Double.MIN_VALUE;        }return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));    }}

运行结果:

88可能重复89可能重复90可能重复91可能重复92可能重复93可能重复94可能重复95可能重复96可能重复97可能重复98可能重复99可能重复

本篇博客到这里就结束了,谢谢大家。写作不易,坚持更难,如大家喜欢就帮忙推送给其他人!最近热文:1、Tomcat 又爆出高危漏洞!8.5 ~ 10 中招…2、Spring Boot 干掉了 Maven 拥抱 Gradle!3、打破你的认知,数字除以0一定会崩溃吗?4、写了个全局变量的bug,被同事们打脸!5、Java 14 祭出神器,Lombok 被干掉了?6、为什么 Redis 单线程能达到百万+QPS?7、Spring Boot 2.3 优雅关闭新姿势,真香!8、玩大发了,Tomcat 8.5 升级有坑…9、我天!xx.equals(null) 是什么骚操作??10、Spring Boot 2.3.1 发布, 10 个新特性!扫码关注Java技术栈公众号干货。

点击「」获取面试题大全~

java 布隆过滤器_牛逼哄哄的布隆过滤器,到底有什么用?相关推荐

  1. idea本地跑如何看gc日志_牛逼了!用 IDEA 扒出了开源组件导致FGC的原因

    某天上午收到最近发布的一个服务频繁FGC的告警,这个服务只是给公司内部相关人员使用的,并非给互联网用户提供服务的系统.而且功能也比较简单,就是查看一些统计信息.报表数据.数据导出Excel等,访问量非 ...

  2. idea启动日志在哪里_牛逼了!用 IDEA 扒出了开源组件导致FGC的原因

    点击上方"占小狼的博客",选择"设为星标 最近热文 JDK13的 5个新特性,抢先看,开车都追不上了! 面试官问,使用Dubbo有没有遇到一些坑?我笑了. 蚂蚁面试:字符 ...

  3. 几位阿里朋友重写的Java并发编程,牛逼了

    昨天在黄金时代群里和读者聊机械键盘大 F 的时候,好朋友 cxuan 推了一篇文章,吸引了我的眼球,名叫"太赞了,阿里几位工程师重写了 <Java 并发编程>",我看完 ...

  4. 牛逼的python代码_牛逼啊!一个随时随地写Python代码的神器

    现在学Python的人越来越多,很多小伙伴都非常有激情,利用碎片时间随时随地学习Python, 大家知道Python是一门编程语言,但是学语言光看不练是没有用的.最好能编程并运行,有没有什么好的神器可 ...

  5. mybatis 分页需要的jar包下载_牛逼哄哄的PageHelper分页插件到底牛在哪里?

    你知道的越多,不知道的就越多,业余的像一棵小草! 你来,我们一起精进!你不来,我和你的竞争对手一起精进! 编辑:业余草 urlify.cn/z2IFn2 推荐:https://www.xttblog. ...

  6. java打字游戏代码_牛逼啊!一个随时随地写Python代码的神器

    现在学Python的人越来越多,很多小伙伴都非常有激情.利用碎片时间随时随地学习Python, 大家知道Python是一门编程语言,但是学语言光看不练是没有用的.最好能编程并运行,有没有什么好的神器可 ...

  7. java代码打出一只狗_牛逼!这位程序员开发出一“舔狗”必备神器(代码已开源)!...

    在一个阳光明媚的清晨,我打开窗户呼吸了一口新鲜空气.阳光灿烂,岁月静好,又是一个约女朋友出去爬山吃饭看电影的好日子.想到女朋友的大眼睛,我脸上不禁洋溢起了幸福的微笑. 打开微信,给女朋友发出去一个美好 ...

  8. 字节跳动java笔试题目_牛客网--字节跳动面试题--特征提取

    牛客网--字节跳动面试题--特征提取 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 来源 链接:特征提取 来源:牛客网 题目 ...

  9. 查询a表有但是b表没有的数据_牛逼!一个上亿数据的报表竟然能做到秒查~

    数据背景 首先项目是西门子中国在我司实施部署的MES项目,由于项目是在产线上运作(3 years+),数据累积很大.在项目的数据库中,大概上亿条数据的表有5个以上,千万级数据的表10个以上,百万级数据 ...

最新文章

  1. SQL语句 goto
  2. php文字左右滚动代码,JavaScript
  3. 一个容易被忽视的css选择器
  4. 从零开始学视觉Transformer (9):自监督ViT算法:BeiT和MAE
  5. 如果用户计算机已接入,01计算机基础知识题(50道)
  6. 使用vue-cli初始化项目时运行‘npm run dev’报错
  7. k8s核心技术-Pod(调度策略)_创建Pod流程_比如一个nginx的Pod创建后如何被分配到某个节点上---K8S_Google工作笔记0024
  8. android 输入法 监听,android 监听 输入法
  9. @spoj - lcs2@ Longest Common Substring II
  10. 总是找不到电脑文件?学会这 3 个 Mac 搜索技巧,一秒就能找到
  11. 58同城推荐系统设计与实现
  12. 解决 Adobe 系列绿色版本无法打开的问题
  13. 李育辉组织行为学理论框架
  14. 小知识 定位测绘领域中全站仪/接收机RTK精度1cm+1ppm的含义
  15. 微信小程序地图如何显示附近厕所WC步行路线
  16. 开发工具:第六章:Java开发者相关的所有软件安装包(35.55GB的资源)
  17. php如何处理查询请求,PHP的curl查看header信息的功能(包括查看返回header和请求header)...
  18. 电影「哪吒之魔童降世」免 费 高 清 完 整 版 在 线 观 看
  19. DataView详解
  20. 小程序与共享图书的融合

热门文章

  1. web——Tomcat Maven插件及Servlet入门
  2. 西门子主程序调用子程序_S7200Smart 子程序局部变量使用教程
  3. 【转】基于DCMTK的DICOM相关程序编写攻略
  4. 【转】细说.NET中的多线程 (六 使用MemoryBarrier,Volatile进行同步)
  5. 关于C#程序的单元测试
  6. ELK Stack 与 Elastic Stack 的异同点
  7. HTML 链接 强制打开“另存为...”弹出式文本链接打开HTML
  8. linux安装静默安装was7,WAS7.0 - 安装并升级WAS7.0.0.31(静默安装)
  9. webstorm 内存溢出怎么弄_webstrom 内存溢出,软件崩溃卡死解决的方法
  10. view [bootstrap-4] not found如何解决_Dubbo如何处理业务异常,这个一定要知道哦