Bloom过滤器

news2025/1/16 15:00:47

Bloom过滤器

  • 一、概述
  • 二、原理
  • 三、优缺点
    • 1. 优点
    • 2.缺点
  • 四、Bloom过滤器在比特币中的应用
  • 五、项目应用步骤
    • 1. pom.xml引入依赖
    • 2. 样例代码
  • 六、Java版简易实现

一、概述

Bloom过滤器是一个允许用户描述特定的关键词组合而不必精确表述的基于概率的过滤方法。它能让用户在有效搜索关键词的同时保护他们的隐私。
1970年,它由布隆提出的。实际上它是由一个很长的二进制向量和一系列随意映射函数组成。它是一种基于概率的数据结构,主要用来判断某个元素是否在集合内,它具有运行速度快(时间效率),占用内存小的优点(空间效率),但是有一定的误识别率和删除困难的问题。它能够告诉你某个元素一定不在集合内或可能在集合内。
在比特币简单支付验证节点(SPV节点)里,这一方法被用来向对等节点发送交易信息查询请求,同时交易地址不会被暴露。
在设计网络爬虫时,我们用它来判断一个网址是否已经被访问过。
反垃圾邮件时,用它来判断一个邮件地址是否在数十亿个垃圾邮件黑名单列表中。
它还被用于解决缓存穿透问题……

比特币节点小故事

打个比方来说,每个全节点就像是一个在陌生城市里的游客,他带着一张包含每条街道、每个地址的详细地图。
相比之下,SPV节点就像是这名陌生城市里的游客只知道一条主干道的名字,通过随机询问该城市的陌生人来获取分段道路指示。
虽然两种游客都可以通过实地考察来验证一条街是否存在,但没有地图的游客不知道每个小巷中有哪些街道,也不知道附近还有什么其他街道。没有地图的游客在“教堂街23号”的前面,并不知道这个城市里是否还有其他若干条“教堂街23号”,也不知道面前的这个是否是要找的那个。
对他来说,最好的方式就是向足够多的人问路,并且希望其中一部分人不是要试图抢劫他。

二、原理

Bloom过滤器的实现是由一个可变长度(N)的二进制数组(N位二进制数构成一个位域)和数量可变(M)的一组哈希函数组成。这些哈希函数的输出值始终在1和N之间,该数值与二进制数组相对应。并且该函数为确定性函数,也就是说任何一个使用相同Bloom过滤器的节点通过该函数都能对特定输入得到同一个的结果。Bloom过滤器的准确性和私密性能通过改变长度(N)和哈希函数的数量(M)来调节。
下面,我用一个小型的十六位数组和三个哈希函数来演示Bloom过滤器的应用原理。
在这里插入图片描述
Bloom过滤器数组里的每一个数的初始值为零。关键词被加到Bloom过滤器中之前,会依次通过每一个哈希函数运算一次。该输入经第一个哈希函数运算后得到了一个在1和N之间的数,它在该数组(编号依次为1至N)中所对应的位被置为1,从而把哈希函数的输出记录下来。接着再进行下一个哈希函数的运算,把另外一位置为1;以此类推。当全部M个哈希函数都运算过之后,一共有M个位的值从0变成了1,这个关键词也被“记录”在了Bloom过滤器里。
在这里插入图片描述

增加第二个关键词就是简单地重复之前的步骤。关键词依次通过各哈希函数运算之后,相应的位变为1,Bloom过滤器则记录下该关键词。需要注意的是,当Bloom过滤器里的关键词增加时,它对应的某个哈希函数的输出值的位可能已经是1。这种情况下,该位不会再次改变。也就是说,随着更多的关键词指向了重复的位,Bloom过滤器随着位1的增加而饱和,准确性也因此降低了。该过滤器之所以是基于概率的数据结构,就是因为关键词的增加会导致准确性的降低。准确性取决于关键字的数量以及数组大小(N)和哈希函数的多少(M)。更大的数组和更多的哈希函数会记录更多的关键词以提高准确性。而小的数组及有限的哈希函数只能记录有限的关键词从而降低准确性。
在这里插入图片描述

为测试某一关键词是否被记录在Bloom过滤器中,我们将该关键词逐一代入各哈希函数中运算,并将所得的结果与原数组进行对比。如果所有的结果对应的位都变为了1,则表示这个关键词有可能已被该过滤器记录。之所以这一结论并不确定,是因为这些字节1也有可能是其他关键词运算的重叠结果。简单来说,Bloom过滤器正匹配代表着“可能是”。
在这里插入图片描述
上图是一个验证关键词“X”是否在前述Bloom过滤器中的图例。相应的比特位都被置为1,所以这个关键词很有可能是匹配的。
另一方面,如果我们代入关键词计算后的结果某位为0,说明该关键词并没有被记录在过滤器里。负匹配的结果不是可能,而是一定。也就是说,负匹配代表着“一定不是”。
在这里插入图片描述

上图是一个验证关键词“Y”是否存在于简易Bloom过滤器中的图例。图中某个结果字段为0,该字段一定没有被匹配。
比特币改进协议BIP0037里已经对Bloom过滤器的实现有所描述。具体请参见GitHub。

三、优缺点

1. 优点

常用的数据结构,如hashmap,set,bit array都能用来快速测试一个元素是否存在于一个集合中,相对于这些数据结构,Bloom过滤器有什么优势呢?
相比于哈希表、链表等数据结构,其空间和时间的优势明显。而且Bloom过滤器的插入、查询时间都是常数O(k),也就是说每次想要插入或查询一个元素是否在集合中时,只需要使用k个哈希函数对元素求值,并将对应的比特位标记或检查对应的比特位即可。
另外, 哈希函数相互之间没有关系,方便由硬件并行实现。Bloom过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

  • 对于hashmap,其本质上是一个指针数组,一个指针的开销是sizeof(void *),在64bit的系统上是64个bit,如果采用开链法处理冲突的话,又需要额外的指针开销,而对于Bloom过滤器来讲,返回可能存在的情况中,如果允许有1%的错误率的话,每个元素大约需要10bit的存储空间,整个存储空间的开销大约是hashmap的15%左右(数据来自维基百科)
  • 对于set,如果采用hashmap方式实现,情况同上;如果采用平衡树方式实现,一个节点需要一个指针存储数据的位置,两个指针指向其子节点,因此开销相对于hashmap来讲是更多的
  • 对于bit array,对于某个元素是否存在,先对元素做hash,取模定位到具体的bit,如果该bit为1,则返回元素存在,如果该bit为0,则返回此元素不存在。可以看出,在返回元素存在的时候,也是会有误判的,如果要获得和Bloom过滤器相同的误判率,则需要比Bloom过滤器更大的存储空间

Bloom过滤器可以表示全集,其它任何数据结构都不能;

  1. 全量存储但是不存储数据本身,适合有保密要求的场景
  2. 空间复杂度为O(m),不会随着元素增加而增加,占用空间少
  3. 插入和查询时间复杂度都是 O(k), 不会随着元素增加而增加,远超一般算法。

2.缺点

Bloom过滤器的缺点和优点一样明显。误判率是其中之一。随着存入的元素数量增加,误判率随之增加。但是如果元素数量太少,则使用散列表足矣。

另外,一般情况下不能从Bloom过滤器中删除元素。我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加1, 这样删除元素时将计数器减掉就可以了。然而要保证安全地删除元素并非如此简单。首先我们必须保证删除的元素的确在Bloom过滤器里面,而Bloom过滤器只能给出可能在集合中或者一定不在集合中的回复,无法给出是否一定在集合中的回复。这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。

  • 相对于hashmap和set,Bloom过滤器在返回元素可能存在的情况中,有一定的误判率,这时候,调用者在误判的时候,会做一些不必要的工作,而对于hashmap和set,不会存在误判情况
  • 对于bit array,Bloom过滤器在插入和查找元素是否存在时,需要做多次hash,而bit array只需要做一次hash,实际上,bit array可以看做是Bloom过滤器的一种特殊情况。

在降低误算率方面,有不少工作,使得出现了很多布隆过滤器的变种。

  • 存在误算率,数据越多,误算率越高
  • 一般情况下无法从过滤器中删除数据
  • 二进制数组长度和 hash 函数个数确定过程复杂

四、Bloom过滤器在比特币中的应用

比特币中Bloom过滤器是在BIP-0037中提到。下面通过“SPV节点如何知道有多少钱”的问题来介绍Bloom过滤器在比特币中的应用。这个问题其实就是“SPV节点如何知道有多少UTXO”

在比特币网络中主要的两种节点类型:

  • 全节点:存放所有区块数据和交易
  • SPV节点:只下载区块头和交易相关部分的局部视图

我们假设,SPV节点最开始只存储了私钥,没有任何其他数据。那么它要获取跟自己地址相关的UTXO,只能向比特币网络中相邻的全节点询问。询问的方式有三种:

  1. 下载完整的区块链账本,自己查找
    这种方法很简单,也能隐藏用户的隐私(全节点无法知道SPV节点关联的钱包的地址)。但是在手机端是不现实的,每次用户需要下载上百G的区块链数据,才能知道自己钱包有多少钱,虽然保护了用户隐私,但是浪费了存储空间和带宽。所以这种方法不行,而这也是为什么有SPV的概念存在,中本聪也是考虑到移动支付的场景的。
  2. 直接告诉全节点自己钱包的所有地址,全节点返回所有跟钱包地址相关的UTXO
    这种方法直接等于是泄露了用户隐私,其他全节点就知道SPV节点所关联的钱包地址。但是好处是所要下载的数据少了很多,也更精确了。
  3. 告诉全节点部分自己钱包的地址信息,全节点返回可能相关的UTXO
    这种方法实际上就是采用Bloom过滤器的方法隐藏用户隐私,从而做到即保护用户隐私,又节省存储空间和带宽。我们知道布隆过滤器的两个特点:只能告诉你某个元素可能存在集合中以及某个元素一定不存在集合中。这里可以简单理解Bloom过滤器用来过滤不属于钱包的UTXO。

SPV节点会以Bloom过滤器的形式告诉相邻全节点自己地址信息,那么根据Bloom过滤器的特性,会有两种结果:

  • 没有通过Bloom过滤器过滤出来的UTXO,就【一定】不属于钱包地址
  • 通过Bloom过滤器过滤出来的UTXO,【可能】属于钱包地址
    这种方法在一定程度上保护用户隐私,节省了存储空间和带宽。但是根据Bloom过滤器的特点,随着钱包交易的UTXO越多,布隆过滤器误报率会越高,也就是相邻全节点返回正确的UTXO概率越低。

五、项目应用步骤

Bloom过滤器只是一个工具,不需要自己实现。本着有车轮就直接拿来用的原则,我们可以使用谷歌帮我们实现的BloomFilter,它封装的非常好,使用起来也非常简洁方便。

1. pom.xml引入依赖

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>27.0.1-jre</version>
</dependency>

由于存在漏洞,不推荐使用该版本,请自行升级为最新版本。
当前最新版本为33.0.0-jre,由于网络不好,暂时没有拉取。

2. 样例代码

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

/**
 * BloomFilter 测试
 *
 * @author Bin
 * @version 1.0
 * 2023/12/23
 */
public class BloomFilterTest {
    public static void main(String[] args) {
        int size = 100_0000;
        BloomFilter<Integer> filter = BloomFilter.create(Funnels.integerFunnel(), size);
//        filter = BloomFilter.create(Funnels.integerFunnel(), size, 0.01);
//        filter = BloomFilter.create(Funnels.integerFunnel(), size, 0.0001);

        System.out.println("初始化Bloom过滤器,添加[1-" + size + "]中的数据到过滤器中");
        for (int i = 1; i <= size; i++) {
            filter.put(i);
        }

        test(filter, 1, size);
        test(filter, size + 1, size * 2);
    }

    private static void test(BloomFilter<Integer> filter, int start, int end) {
        int exist = 0;
        int exclude = 0;
        for (int i = start; i <= end; i++) {
            if(filter.mightContain(i)) {
                exist ++;
            } else {
                exclude ++;
            }
        }

        String str = "逐个判断[%d - %d]中的数据,被判为存在和不存在的个数分别是:%d / %d\r\n";
        System.out.printf(str, start, end, exist, exclude);
    }
}

六、Java版简易实现

虽说车轮不用重复造,但是想了解底层除了看源码,还就是自己造轮子。

Talk Is Cheap, Show Me The Code.


import java.util.BitSet;

/**
 * 简易版本Bloom Filter
 *
 * @author Bin
 * @version 1.0
 * 2023/12/23
 */
public class BloomFilter {
    /** 二进制数组 */
    private final BitSet bits;
    /** 二进制向量(数组)的位数 */
    private final int size;
    /** 用于生成信息指纹的随机数 */
    private final int[] seeds;

    public BloomFilter() {
        this(Integer.MAX_VALUE, new int[]{2, 3, 5, 7, 11}); // 默认大小为全部整数,种子为质数
    }

    public BloomFilter(int size, int[] seeds) {
        if (size < 1) {
            throw new IllegalArgumentException("Size must be greater than zero");
        }
        this.size = size;
        this.seeds = seeds;
        this.bits = new BitSet(size);
    }

    public void add(int item) {
        add(Integer.toString(item));
    }

    public void add(String item) {
        for (int seed : seeds) {
            int hash = hashFunction(seed, item);
            int index = hash % size;
            bits.set(index, true);
        }
    }

    public boolean contains(int item) {
        return contains(Integer.toString(item));
    }

    public boolean contains(String item) {
        if (item == null) {
            return false;
        }
        boolean result = true;
        for (int seed : seeds) {
            int hash = hashFunction(seed, item);
            int index = hash % size;
            result &= bits.get(index);
        }
        return result;
    }

    private int hashFunction(int seed, String item) {
        int hash = 0;
        for (char c : item.toCharArray()) {
            hash += seed * c;
        }
        return Math.abs(hash);
    }

    public static void main(String[] args) {
        BloomFilter filter = new BloomFilter();
        // 存入数据
        int size = 1000_0000;
        for (int i = 0; i < size; i++) {
            filter.add(i);
        }

        // 查看已有数据是否存在情况
        int count = 0;
        for (int i = 0; i < size; i++) {
            if(filter.contains(i)) {
                count ++;
            }
        }
        System.out.println("count=" + count);

        // 查看其它数据是否存在情况
        count = 0;
        for (int i = size; i < size * 2; i++) {
            if(filter.contains(i)) {
                count ++;
            }
        }
        System.out.println("count=" + count);
    }
}

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

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

相关文章

新型智慧城市解决方案:PPT全文56页,附下载

关键词&#xff1a;智慧城市解决方案&#xff0c;智慧城市管理技术&#xff0c;智慧城市建设&#xff0c;数字城市建设 一、智慧城市宏观形势 1、政策支持&#xff1a;出台了一系列政策&#xff0c;鼓励和支持智慧城市的发展。这些政策为智慧城市的建设提供了政策保障和资金支…

Confluent 与阿里云将携手拓展亚太市场,提供消息流平台服务

10 月 31 日&#xff0c;杭州云栖大会上&#xff0c;阿里云云原生应用平台负责人丁宇宣布&#xff0c;Confluent 成为阿里云技术合作伙伴&#xff0c;合作全新升级&#xff0c;一起拓展和服务亚太市场。 本次合作伙伴签约&#xff0c;阿里云与消息流开创领导者 Confluent 将进一…

Zabbix6 使用Agent2实现证书监控的详细步骤

目标 我们的目标是通过获取网站的证书信息来实现网站证书监控。 使用agent2的key 只需使用其中的key&#xff0c;就能实现我们的目标功能。然而&#xff0c;由于它返回的是json格式的数据&#xff0c;我们需要根据数据来配置监控项目&#xff08;item&#xff09;和触发器&am…

Jenkins自动化部署之后端

准备工作参考本人另外几篇Jenkins相关的文章 新建任务 添加参数配置 字符串参数&#xff1a;分支名称 多选框&#xff1a;项目名称&#xff08;Extended Choice Parameter插件必备&#xff0c;插件安装参考我另外的文章&#xff09; 这个分割规则自定义。只要根据Jenkins…

Linux Centos-7.5_64bit 系统等保测评内容

一、身份鉴别 a) 应对登录的用户进行身份标识和鉴别&#xff0c;身份标识具有唯一性&#xff0c;身份鉴别信息具有复杂度要求并定期更换 指引&#xff1a;1.观察管理员登录方式。2.查看系统配置&#xff0c;记录配置参数。&#xff08;more /etc/pam.d/system-auth、more /et…

Conda命令、Miniconda3基础安装使用教程

Conda命令、Miniconda3基础安装使用教程 Minicodna3下载地址&#xff1a;https://docs.conda.io/projects/miniconda/en/latest/index.html 正常安装完毕后&#xff0c;出现conda的控制台&#xff1a; 默认的源下载太慢&#xff0c;换清华源&#xff0c;依次执行以下命令&…

Python内置类属性__class__属性的使用教程

概要 Python作为一种高级编程语言&#xff0c;提供了丰富的功能和灵活性&#xff0c;使得开发人员能够更加方便地处理各种任务。其中一个强大的功能是内置类属性__class__属性。本文将详细介绍__class__属性的用法&#xff0c;帮助读者更好地理解和利用这一功能。 第一部分&am…

直播种类之VR全景直播

VR全景直播是一种结合虚拟现实技术和直播的全新体验。通过全景相机拍摄&#xff0c;观众可以身临其境地感受直播现场的氛围和真实感&#xff0c;提供逼真的观看体验。 VR全景直播的优势在于: 1.身临其境的观看体验:观众可以像真的在现场一样&#xff0c;通过虚拟现实设备探索…

使用Ubuntu22+Minikube快速搭建K8S开发环境

安装Vmware 这一步&#xff0c;可以参考我的如下课程。 安装Ubuntu22 下载ISO镜像 这里我推荐从清华镜像源下载&#xff0c;速度会快非常多。 下载地址&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/22.04.3/ 如果你报名了我的这门视频课程&#xf…

九州金榜家庭教育面对孩子负面情绪父母如何做?

情绪管理这个词越来越高频的出现在各个场合&#xff0c;越来越多的人注意到了情绪对人的影响&#xff0c;情绪可以让一个平时理智的人冲动的犯下错误&#xff0c;可以让一个懦弱的人充满勇气&#xff0c;可以让一个聪明的人失去判断&#xff0c;可以让一个消极的人重新找回积极…

业绩的非线性特征——捕获指标的改进及国内基金行业的证据

摘要及声明 1&#xff1a;本文主要对捕获能力指标进行改进&#xff0c;并且利用改进的指标进行实证检验&#xff1b; 2&#xff1a;本文主要为理念的讲解&#xff0c;模型也是笔者自建&#xff0c;文中假设与观点是基于笔者对模型及数据的一孔之见&#xff0c;若有不同见解欢…

分享70个Java源码总有一个是你想要的

分享70个Java源码总有一个是你想要的 学习知识费力气&#xff0c;收集整理更不易。 知识付费甚欢喜&#xff0c;为咱码农谋福利。 源码下载链接&#xff1a;https://pan.baidu.com/s/1uyWfeUuO_4jRbAEw825qRw?pwd6666 提取码&#xff1a;6666 项目名称 CRUD is ReallyU…

交通流预测 | Matlab基于KNN-BiLSTM的交通流预测(对比SVR、LSTM、GRU、KNN-LSTM)

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 交通流预测 | Matlab基于KNN-BiLSTM的交通流预测&#xff08;对比SVR、LSTM、GRU、KNN-LSTM&#xff09; 程序设计 完整程序和数据获取方式&#xff1a;私信博主回复Matlab基于KNN-BiLSTM的交通流预测&#xff08;对…

国图公考:公考常识题如何复习更高效?

公务员考试是许多人求职的首选之一&#xff0c;而公考常识题作为公务员考试的重要组成部分&#xff0c;对于考生来说具有很高的分值。在这一模块的复习中&#xff0c;有以下几点可以帮助考生提高学习效率&#xff0c;一起来看一下吧! 一、明确复习目标 了解自己在各个知识点上…

【XML】TinyXML 详解(二):接口详解

【C】郭老二博文之&#xff1a;C目录 1、XML测试文件&#xff08;laoer.xml&#xff09; <?xml version"1.0" standalone"no" ?> <!-- Hello World !--> <root><child name"childName" id"1"><c_child…

基于docker-compose 安装Sonar并集成gitlab

文章目录 1. 前置条件2. 编写docker-compose-sonar.yml文件3. 集成 gitlab4. Sonar Login with GitLab 1. 前置条件 安装docker-compose 安装docker 创建容器运行的特有网络 创建挂载目录 2. 编写docker-compose-sonar.yml文件 version: "3" services:sonar-postgre…

如何更好地理解和掌握 KMP 算法?

KMP算法是一种字符串匹配算法&#xff0c;可以在 O(nm) 的时间复杂度内实现两个字符串的匹配。本文将引导您学习KMP算法&#xff0c;阅读大约需要30分钟。 1、字符串匹配问题 所谓字符串匹配&#xff0c;是这样一种问题&#xff1a;“字符串 P 是否为字符串 S 的子串&#xf…

Python入门学习篇(六)——for循环while循环

1 for循环 1.1 常规for循环 1.1.1 语法结构 for 变量名 in 可迭代对象:# 遍历对象时执行的代码 else:# 当for循环全部正常运行完(没有报错和执行break)后执行的代码1.1.2 示例代码 print("----->学生检查系统<------") student_lists["张三",&qu…

TensorRT-Alpha FAQ

1、linux下出现错误&#xff1a; libyolov8.so: undefined reference to sample::splitToStringVec(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char) collect2: error: ld returned 1 exit status CMa…

FPC柔性线路板使用UV胶水的优势有哪些?

UV胶水在FPC柔性线路板的装配中具有明显的优势&#xff1a; 快速固化 UV胶水在紫外线照射后10秒左右迅速固化&#xff0c;因此它能够在短时间内完成连接。这非常有助于实际工业作业中提高生产效率&#xff0c;特别是在需要大批量生产的情况下。 精确控制固化时间 UV胶水的固…