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

65c4cb399aa4e6eb04377d3cbf615f10.png

Java技术栈

www.javastack.cn

打开网站看更多优质文章

作者:CodeBear的园子

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

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

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

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

布隆过滤器的应用

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

缓存穿透

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

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

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

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

什么是布隆过滤器

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

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

现在需要添加一个数据:

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

4bc633fb94b7e738e50b9ff02af83c66.png

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

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

这样,刚才添加的数据就占据了布隆过滤器“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次,我们计算下误判率:

3a8fdd29e149deb9b8341f0efc6b5fb3.png

和我们定义的期望误判率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技术栈公众号干货。

34f063b0c3ddc9c9cc40518300a019d0.png

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446307.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java web后端6 java Bean EL表达式

EL表达式和JSTL概述 java Bean规范 java中成员变量使用类Integer private Integer count; java Bean的创建 创建java Bean: BookTest.java package com.example.elandjstl.bean;public class BookTest {//java中成员变量使用类Integerprivate Integer count;private Boolean…

python根须系统斜杠_深入浅出Python中的os模块

「Author&#xff1a;Runsen」当初学Python的时候&#xff0c;把一些标准库和第三方开源库学的七零八落&#xff0c;不成系统&#xff0c;正好趁这个机会来系统的整理一下&#xff0c;先从Python常用的标准库os开始吧。osOS模块简单的来说它是一个Python的系统编程的操作模块&a…

Java web后端7JSTL

概括 下载jstl的jar包 官网&#xff1a;https://mvnrepository.com/ 网址1&#xff1a;https://search.maven.org/ 在pomxml中插入依赖&#xff1a; <dependency><groupId>taglibs</groupId><artifactId>standard</artifactId><version>1…

Python学习14 模块和包

模块 公共类、函数都可以放在独立的文件中&#xff0c;这样其他多个程序都可以使用&#xff0c;而不必把这些公共性的类、函数等在每个程序中复制一份&#xff0c;这样独立的文件就叫做模块&#xff0c;它们的扩展名为.py 标准库中的模块 使用help查看模块 代码&#xff1a; …

python语句分为_python以什么划分语句块

语句块是在条件为真&#xff08;条件语句&#xff09;时执行或者执行多次&#xff08;循环语句&#xff09;的一组语句&#xff1b;在代码前放置空格来缩进语句即可创建语句块&#xff0c;语句块中的每行必须是同样的缩进量&#xff1b;&#xff08;推荐学习&#xff1a;Python…

Python学习15 正则表达式1

网址 正则表达式测试网址&#xff1a;https://regex101.com/ 概述 正则表达式&#xff1a; 正则表达式(Regular Expression)是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff09;和特殊字符&#xff08;称为"元字符"…

STL源码剖析 空间配置器 查漏补缺

ptrdiff_t含义 减去两个指针的结果的带符号整数类型ptrdiff_t (Type support) - C 中文开发手册 - 开发者手册 - 云社区 - 腾讯云 std::set_new_handler&#xff08;&#xff09;函数的理解 关于set_new_handler的理解_wck0617-CSDN博客new分配内存的时候 如果分配的空间不…

Python学习16 正则表达式2 re模块

re 模块 re 模块&#xff1a; Python的 re 模块实现了正则表达式处理的功能。 导入re模块后&#xff0c;使用findall、search函数可以进行匹配 查找&#xff1a;match和search 多个匹配上的&#xff0c;也只会返回第一个匹配上的 re.match()&#xff1a; 需要特别注意的是&…

STL源码剖析 内存基本处理工具 初始化空间的五个函数

初始化空间的五个函数构造函数 construct()析构函数 destroy()剩余三个底层函数 和 高层函数之间的对应关系如下uninitialized_copy() 对应 copy()uninitialized_fill() 对应 fill()uninitialized_fill_n() 对应 fill_n()使用<memory>使用上述三个底层函数 uninitiali…

单基因gsea_筛到5分的核心基因以后你可以怎么做?

这一次我们从一些已经发表的文章拆解&#xff0c;我们来看看&#xff0c;你找到了一个核心基因以后&#xff0c;你可以怎么做呢&#xff1f;我们就不说那么多废话了&#xff0c;直接用几篇文章的解读来带着大家领会一下如何去进行下一步的分析。Case1&#xff1a;预后标志物免疫…

STL源码剖析 迭代器的概念和traits编程技法

迭代器&#xff1a;依序巡防某个聚合物(容器)所含的各个元素&#xff0c;但是不需要暴露这个聚合物的内部表述方式核心思想&#xff1a;将容器和算法分开&#xff0c;彼此独立设计容器和算法的泛型化&#xff0c;均可以使用模板&#xff0c;使用迭代器连接容器和算法例子 templ…

STL源码剖析 5中迭代器型别

最常使用的5种迭代器的型别 为 value_type、difference_type、pointer、reference、iterator_category。如果想要自己开发的容器和STL进行适配&#xff0c;就需要定义上述5种类型 iteraor_traits 必须针对传入的型别为 pointer 或者 pointer-to-const设计偏特化版本 template &…

加载tf模型 正确率很低_深度学习模型训练全流程!

↑↑↑关注后"星标"Datawhale每日干货 & 每月组队学习&#xff0c;不错过Datawhale干货 作者&#xff1a;黄星源、奉现&#xff0c;Datawhale优秀学习者本文从构建数据验证集、模型训练、模型加载和模型调参四个部分对深度学习中模型训练的全流程进行讲解。一个成…

Python学习17 Turtle库绘图

学习网址&#xff1a;https://docs.python.org/zh-cn/3/library/turtle.html Turtle库 Turtle库是Python语言中一个很流行的绘制图像的函数库&#xff0c;一个小乌龟&#xff0c;在一个横轴为x、纵轴为y的坐标系原点&#xff08;画布中心&#xff09;&#xff0c;(0,0)位置开…

android ros 节点编写_嵌入式的我们为什么要学ROS

前言本来是要写一篇STM32移植ROS的一个小lib库&#xff0c;ROS一般都是需要跑在Linux上的&#xff0c;STM32使用就是当成一个ROS通讯的小节点&#xff0c;但是写文章时间不够&#xff0c;所以就简单做一篇ROS的介绍文章&#xff0c;分享给嵌入式的小伙伴们。ROS现在在机器人领域…

STL源码剖析 __type_traits

traits编程 弥补了C本身的不足STL只对迭代器进行规范制定出了iterator_traits&#xff0c;SGI在此基础上进一步扩展&#xff0c;产生了__type_traits双下划线的含义是这个是SGI内部使用的东西&#xff0c;不属于STL标准iterator_traits 负责萃取迭代器的特性__type_traits负责萃…

STL源码剖析 序列式容器|Vector

容器的概观和分类 array 数组 、list 链表、tree树 、stack堆栈、queue队列、hash table散列表、set集合、map映射表根据数据在容器中的排列顺序&#xff0c;将上述数据结构分为序列式和关联式两种类型SGI STL使用内缩方式来表达基层和衍生层之间的关系衍生不是派生&#xff0…

ansible 修改文件变量_Ansible Playbook中的变量与引用

Ansible是一个系列文章&#xff0c;我会尽量以通俗易懂、诙谐幽默的总结方式给大家呈现这些枯燥的知识点&#xff0c;让学习变的有趣一些。Ansible自动化运维前言前面有说到使用playbook来搞一些复杂的功能&#xff0c;我们使用YAML来写playbook&#xff0c;就像我们用其它语言…

STL源码剖析 list概述

目录 list的节点(node) list迭代器 list 的构造和内存管理 list 的元素操作 list相较于vector连续的线性空间就显得很复杂&#xff0c;他的存储空间是不连续的&#xff0c;好处是每次插入和删除一个元素的时候&#xff0c;只需要配置或者释放一个元素的空间 插入和删除十分的…

vsftp不允许切换到其它目录_IntelliJ IDEA如何对project的目录进行筛选显示?

如果你的项目很庞大&#xff0c;同一个功能用到的各种文件散落在多个文件夹&#xff0c;开发时切换不便&#xff0c;可以利用scope功能&#xff0c;只显示该功能用到的文件&#xff0c;让project列表十分清爽&#xff0c;提高开发效率。本文使用的IDEA版本为2020.1。1、打开sco…