布隆过滤器原理和使用

布隆过滤器介绍及实战应用(防止缓存穿透) 本文将讲解布隆过滤器的概念、原理、优缺点、使用场景;通过Java使用布隆过滤器,以及在项目中结合Redis使用,贴近实战,干货满满!!! 阅读详情

转载:https://zhuanlan.zhihu.com/p/692179021

一、背景介绍

在互联网中,我们经常遇到需要在大量数据中判断目标数据是否存在的情况。例如,在网络爬虫中,我们需要判断某个网址是否已经被访问过。为了实现这一功能,通常需要使用一个容器来存储已访问过的网址。如果将这些数据直接存储在磁盘中,每次判断都要进行磁盘查询,这将导致大量的IO操作,效率较低。因此,我们希望将这些数据保存在内存中。在数据量较小的情况下,可以使用Redis来存储这些数据。但是,当数据量超过上千万时,将会消耗几GB甚至几十GB的内存空间。然而,对于仅需要记录数据是否存在的情况而言,这样使用大量内存显然是浪费的。为了解决这个问题,我们可以使用布隆过滤器(Bloom Filter)。布隆过滤器是一种占用空间少且时间效率高的工具。

二、认识布隆过滤器

2.1 布隆过滤器简介

布隆过滤器(Bloom Filter)是1970年由布隆提出的,它实质上是一个很长的二进制向量和一系列随机映射函数 (Hash函数)。

作用:它是一个空间效率高的概率型数据结构,用来告诉你:一个元素一定不存在或者可能存在。

2.2 优点

  • 相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入/查询时间都是常数(即hash函数的个数)。

  • Hash 函数相互之间没有关系,方便由硬件并行实现。

  • 布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

  • 布隆过滤器可以表示全集,其它任何数据结构都不能。解释:布隆过滤器(Bloom Filter)可以“表示全集”的说法,源于其独特的概率特性。具体来说,布隆过滤器并不存储元素本身,而是通过哈希函数映射到一个位数组,形成一个“模糊但紧凑”的数据表示。因此,它具备以下特性,使得可以“表示全集”

    1. 误判但无遗漏:

      • 布隆过滤器可以确定某个元素“可能存在”或“一定不存在”。
      • 对于所有可能的输入,布隆过滤器都能做出判断,因此在逻辑上,它可以表示整个可能的输入集合(即“全集”)。
      • 由于存在哈希碰撞,可能会误判一个不存在的元素为“存在”,但不会漏掉已加入的元素。
    2. 有限存储表示无限集合:

      • 传统数据结构(如哈希表、集合)需要存储元素本身,无法表示“无限集合”。
      • 布隆过滤器通过固定大小的位数组和哈希函数,即可用于查询任意元素是否在集合中,而不必存储所有元素,从而“间接表示全集”。
    3. 其他数据结构的局限性:

      • 哈希表、数组、链表等都必须存储具体的元素,因此它们只能存储有限大小的集合,而无法表示“全集”。
      • 逻辑上,“全集”意味着可以对任意元素进行查询,而传统数据结构如果不包含某个元素,就无法返回任何有效结果。

    结论:布隆过滤器通过概率方法“模糊地”表示全集,虽然存在误判,但它仍然能处理理论上无限可能的输入,这是其他数据结构无法做到的。因此,在某种意义上,可以说“布隆过滤器可以表示全集,而其他数据结构不能”。

2.3 缺点

  • 有误判率存在。
  • 不支持删除。

2.4 适用场景

  • 预防缓存穿透:布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在。
  • 网络爬虫:布隆过滤器可以用来去重已经爬取过的URL。
  • 邮箱的垃圾邮件过滤。
  • 黑白名单。

三、 布隆过滤器原理

3.1 结构

布隆过滤器实现原理就是一个超大位数的数组和多个不同Hash算法函数。假设位数组的长度为 m,哈希函数的个数为 k。如下图,一个长度16位的数组,3个不同Hash算法函数,数组里面存储的是 bit 位,只放 0 和 1,初始为 0。

不同Hash算法函数

指定长度数组

3.2 添加元素

将要添加的元素分别通过k个哈希函数计算得到k个哈希值,这k个hash值对应位数组上的k个位置,然后将这k个位置设置为1。

我们添加一个data1和data2两个元素,两个元素根据三个hash算法函数计算出的值,需要说明一点三个值可能会存在相同的值。

其中data1计算出1、8、13三值,我们把数组中对应的位置设置为1。

Hash1(data1)=1
Hash2(data1)=8
Hash3(data1)=13

如图:

data2计算出2、5、13三值,我们把数组中对应的位置设置为1

Hash1(data2)=2
Hash2(data2)=5
Hash3(data2)=13

如图:

我们发现data1和data2经过hash函数后,出现了一个相同值,这种是正常的,也正是因为这种情况的存在,需要多个函数来保证每个元素尽可能对应数组位置的唯一性,可以看下两个元素在一起的效果。

如图:

当不同元素在不同或者相同的hash函数计算后,得到同一个值,依旧只需要这个位置保持1即可。

3.3 查询元素

将要查询的元素分别通过k个哈希函数计算得到k个哈希值,这k个hash值对应位数组上的k个位置。如果这k个位置中有一个位置为0,则此元素一定不存在集合中。如果这k个位置全部为1,则这个元素可能存在。

我们在刚才添加过data1和data2两个元素的布隆过滤器查询以下三种元素,data1已添加到布隆过滤器元素,data3和data4都是未添加到布隆过滤器元素。

查询data1先根据添加时的三个hash函数计算分别对应值,值分别是1、8、13,然后查询数组中这三个位置的值是否为1。

Hash1(data1)=1
Hash2(data1)=8
Hash3(data1)=13

如图:

我们可以看到数组中1、8、13这三个位置都是1,data1可能存在于该布隆过滤器。我们从添加的角度来看,我们知道data1是一定存在于该布隆过滤器的,为什么还要是说可能呢,是因为查询出来三个位置都为1不能代表这个三个1都是同一个元素添加的,下面我们看下元素data3的查询。

查询data3先根据添加时的三个hash函数计算分别对应值,值分别是2、8、13,然后查询数组中这三个位置的值是否为1。

Hash1(data3)=2
Hash2(data3)=8
Hash3(data3)=13

如图:

我们已知的该布隆过滤器我们没有添加给data3,为什么data3查询出来三个位置的值都为1呢。我们可以看到data3所命中的位置分别是data2添加时把位置2赋值的1,和data1添加时把位置8和位置13赋值的1,都是由其他元素改变的位置对应的值,所以命中位置全部为1。这个元素可能存在。

我们查询一下data4,看下命中位置不全为0的数据。查询data4先根据添加时的三个hash函数计算分别对应值,值分别是2、8、13,然后查询数组中这三个位置的值是否为1。

Hash1(data4)=1
Hash2(data4)=8
Hash3(data4)=12

如图:

我们可以看到data4元素的hash函数3计算之后的值是12,数组位置12的值是0,没有元素在位置12赋值过1。如果data4存在于该布隆过滤器,则一定在添加data4时会把位置12赋值1,此时位置12还是0,则说明该布隆过滤器未添加过data4元素,所以位置中有一个位置为0。则此元素一定不存在布隆过滤器中。

四、布隆过滤器误判率

刚才查询时我们发现data3没有添加过到布隆过滤器,却在布隆过滤器查询到了,这种情况就是布隆过滤器误判了。那可以不存在误判或者减少误判吗?事实上误判是一定存在的,我们可以尽可能减小误判。下面说下如何得到误判率。

4.1 参数

m:布隆过滤器的bit长度。
n:插入过滤器的元素个数。
k:哈希函数的个数。

4.2 误判率公式(推导过程自查)

1、布隆过滤器(Bloom Filter)的误差率指的是误判率(False Positive Rate, FPR),即某个元素实际上不在集合中,但布隆过滤器错误地判断它存在的概率。

误判率的计算公式为:

其中:

  • k:哈希函数的数量
  • n:插入的元素个数
  • m:布隆过滤器的位数组大小
  • e:自然常数(约等于 2.718)

由误判率公式可知,在k一定的情况下,当n增加时,误判率增加,m增加时,误判率减少。

五、实现方式

5.1 Guava实现

guava是谷歌开源工具类,其中就有能直接实现布隆过滤器的方法,不需要重复造轮子。

方法名功能参数返回值
put添加元素put(T object)boolean
mightContain检查元素是否存在mightContain(T object)boolean
copy根据此实例创建一个新的BloomFiltecopy()BloomFilter
approximateElementCount已添加到Bloom过滤器的元素的数量approximateElementCount()long
expectedFpp返回元素存在的错误概率expectedFpp()double
isCompatible确定给定的Bloom筛选器是否与此Bloom筛选器兼容isCompatible(BloomFilter that)boolean
putAll通过执行的逐位OR将此Bloom过滤器与另一个Bloom过滤器组合putAll(BloomFilter that)void

引入依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>23.0</version>
</dependency>

测试代码

 private static void GuavaBloomFilter() {
    // 创建布隆过滤器对象
    BloomFilter bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()),EXPECTED_INSERTIONS,FALSE_PROBABILITY);
    // 向过滤器中添加元素
    bloomFilter.put("element001");
    bloomFilter.put("element003");
    // 判断元素是否存在
    System.out.println(bloomFilter.mightContain("element001"));//true
    System.out.println(bloomFilter.mightContain("element002"));//false
    // 已添加到Bloom过滤器的元素的数量
    System.out.println(bloomFilter.approximateElementCount());// 2
    // 返回元素存在的错误概率
    System.out.println(bloomFilter.expectedFpp());
}

5.2 Redis实现

  • 开源Redisson(RBloomFilter)。
  • Redis 4.0 官方提供布隆过滤器插件。
  • 通过Redis提供的bitMap自己实现。

5.2.1 开源Redisson方式

Redisson方法

方法名功能参数返回值
add添加元素add(T object)boolean
contains检查元素是否存在contains(T object)boolean
count已添加到Bloom过滤器的元素的数量count()long
getExpectedInsertions返回的预期插入元素的个数getExpectedInsertions()long
getFalseProbability返回元素存在的错误概率getFalseProbability()double
getHashIterations返回每个元素使用的哈希迭代次数getHashIterations()int
getSize返回此实例所需Redis内存的位数getSize()long
tryInit初始化Bloom筛选器参数tryInit(long expectedInsertions, double falseProbability)boolean
delete删除对象delete()boolean

引入依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.22.1</version>
</dependency>

测试代码

private static void RedissonBloomFilter() {
    Config config = new Config();
    config.useSingleServer().setAddress("redis://" + REDIS_IP + ":" + REDIS_PORT);
    config.useSingleServer().setPassword(REDIS_PASSWORD);
    // 获取客户端
    RedissonClient redissonClient = Redisson.create(config);
    RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(BLOOM_FILTER_NAME);
    // 初始化布隆过滤器:预期插入量为100000000L,预期错误概率为1%
    bloomFilter.tryInit(EXPECTED_INSERTIONS, FALSE_PROBABILITY);
    // 插入数据
    bloomFilter.add("element001");
    bloomFilter.add("element003");

    // 判断下面元素是否在布隆过滤器中
    System.out.println(bloomFilter.contains("element002"));//false
    System.out.println(bloomFilter.contains("element001"));//true

    // 已添加到Bloom过滤器的元素的数量
    System.out.println(bloomFilter.count());//2
    // 预期插入元素的个数
    System.out.println(bloomFilter.getExpectedInsertions());//1000000
    // 元素存在的错误概率
    System.out.println(bloomFilter.getFalseProbability());//0.01
    // 每个元素使用的哈希迭代次数
    System.out.println(bloomFilter.getHashIterations());
    // 实例所需Redis内存的位数
    System.out.println(bloomFilter.getSize());
}

5.2.2 Redis 4.0 官方提供布隆过滤器插件

基础命令

命令功能参数
BF.RESERVE创建一个大小为capacity,错误率为error_rate的空的BloomBF.RESERVE {key} {error_rate} {capacity} [EXPANSION {expansion}] [NONSCALING]
BF.ADD向key指定的Bloom中添加一个元素itomBF.ADD {key} {item}
BF.MADD向key指定的Bloom中添加多个元案BF.MADD {key} {item ...}
BF.INSERT向key指定的Bloom中添加多个元素,添加时可以指定大小和错误率,且可以控制在Bloom不存在的时候是否自动创建BF.INSERT {key} [CAPACITY {cap}] [ERROR {error}] [EXPANSION {expansion}] [NOCREATE] [NONSCALING] ITEMS {item ...}
BF.EXISTS检查一个元秦是否可能存在于key指定的Bloom中BF.EXISTS {key} {item}
BF.MEXISTS同时检查多个元素是否可能存在于key指定的Bloom中BF.MEXISTS {key} {item ...}
BF.SCANDUMP对Bloom进行增量持久化操作BF.SCANDUMP {key} {iter}
BF.LOADCHUNK加载SCANDUMP持久化的Bloom数据BF.LOADCHUNK {key} {iter} {data}
http://BF.INFO查询key指定的Bloom的信息http://BF.INFO {key}
BF.DEBUG查看BloomFilter的内部详细信息(如每层的元素个数,错误率等)BF.DEBUG (key}

引入依赖

    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>4.2.0</version>
    </dependency>

测试代码

 private static void RedisBloomFilter() {
    // 建立连接
    BloomFilterCommands bloomFilterCommands = new JedisPooled(REDIS_IP, REDIS_PORT, "", REDIS_PASSWORD);
    // 构建布隆过滤器参数
    BFReserveParams bfReserveParams = new BFReserveParams();
    bfReserveParams.expansion(2);

    // 创建一个过滤器
    String test = bloomFilterCommands.bfReserve(BLOOM_FILTER_NAME, FALSE_PROBABILITY, EXPECTED_INSERTIONS, bfReserveParams);

    // 向过滤器中添加元素
    bloomFilterCommands.bfAdd(BLOOM_FILTER_NAME, "element001");
    bloomFilterCommands.bfAdd(BLOOM_FILTER_NAME, "element003");

    // 判断元素是否存在
    System.out.println(bloomFilterCommands.bfExists(BLOOM_FILTER_NAME, "element001"));//true
    System.out.println(bloomFilterCommands.bfExists(BLOOM_FILTER_NAME, "element002"));//false
}

5.2.3 通过Redis提供的bitMap自己实现

自定义方法

方法名功能参数返回值
add添加元素add(String key, String element, int expireSec)boolean
contains检查元素是否存在contains(String key, String element)boolean
getExpectedInsertions返回的预期插入元素的个数getExpectedInsertions()long
getFalseProbability返回元素存在的错误概率getFalseProbability()double
getNumHashFunctions返回每个元素使用的哈希迭代次数getNumHashFunctions()int
getBitmapLength返回Bitmap长度getBitmapLength()long
BloomFilterUtils创建Bloom对象BloomFilterUtils(long expectedInsertions, double falseProbability)BloomFilterUtils

测试代码

  public class BloomFilterUtils {

    private static final String BF_KEY_PREFIX = "bf_";

    private long numApproxElements;
    private double falseProbability;
    // hash个数
    private int numHashFunctions;
    // 数组长度
    private int bitmapLength;

    private JedisResourcePool jedisResourcePool;

    /**
     * 构造布隆过滤器。注意:在同一业务场景下,三个参数务必相同
     *
     * @param numApproxElements 预估元素数量
     * @param fpp               可接受的最大误差
     * @param jedisResourcePool Codis专用的Jedis连接池
     */
    public BloomFilterUtils(Long numApproxElements, double fpp, JedisResourcePool jedisResourcePool) {
        this.numApproxElements = numApproxElements;
        this.falseProbability = fpp;
        this.jedisResourcePool = jedisResourcePool;
        // 数组长度 m = (n * lnp)/ln2^2
        bitmapLength = (int) (-numApproxElements * Math.log(fpp) / (Math.log(2) * Math.log(2)));
        // hash个数 k = (n / m ) * ln2
        numHashFunctions = Math.max(1, (int) Math.round((double) bitmapLength / numApproxElements * Math.log(2)));
    }

    /**
     * 取得预估元素数量
     */
    public long getExpectedInsertions() {
        return numApproxElements;
    }

    /**
     * 返回元素存在的错误概率
     */
    public double getFalseProbability() {
        return falseProbability;
    }

    /**
     * 取得自动计算的最优哈希函数个数
     */
    public int getNumHashFunctions() {
        return numHashFunctions;
    }

    /**
     * 取得自动计算的最优Bitmap长度
     */
    public int getBitmapLength() {
        return bitmapLength;
    }

    /**
     * 计算一个元素值哈希后映射到Bitmap的哪些bit上
     *
     * @param element 元素值
     * @return bit下标的数组
     */
    private long[] getBitIndices(String element) {
        long[] indices = new long[numHashFunctions];

        // 元素  使用MurMurHash3 128位Hash算法转换值
        byte[] bytes = Hashing.murmur3_128()
                .hashObject(element, Funnels.stringFunnel(Charset.forName("UTF-8")))
                .asBytes();

        // 低8位转Long值
        long hash1 = Longs.fromBytes(
                bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]
        );
        // 高8位转Long值
        long hash2 = Longs.fromBytes(
                bytes[15], bytes[14], bytes[13], bytes[12], bytes[11], bytes[10], bytes[9], bytes[8]
        );

        long combinedHash = hash1;
        // 双重哈希进行散列
        for (int i = 0; i  < numHashFunctions; i++) {
            indices[i] = (combinedHash & Long.MAX_VALUE) % bitmapLength;
            combinedHash += hash2;
        }
        return indices;
    }


    /**
     * 插入元素
     *
     * @param key       原始Redis键,会自动加上'bf_'前缀
     * @param element   元素值,字符串类型
     * @param expireSec 过期时间(秒)
     */
    public void add(String key, String element, int expireSec) {
        if (key == null || element == null) {
            throw new RuntimeException("键值均不能为空");
        }
        String actualKey = BF_KEY_PREFIX.concat(key);

        try (Jedis jedis = jedisResourcePool.getResource()) {
            try (Pipeline pipeline = jedis.pipelined()) {
                // 遍历元素所有hash结果的bit位置
                for (long index : getBitIndices(element)) {
                    pipeline.setbit(actualKey, index, true);
                }
                pipeline.syncAndReturnAll();
            }
            jedis.expire(actualKey, expireSec);
        }
    }

    /**
     * 检查元素在集合中是否(可能)存在
     *
     * @param key     原始Redis键,会自动加上'bf_'前缀
     * @param element 元素值,字符串类型
     */
    public boolean contains(String key, String element) {
        if (key == null || element == null) {
            throw new RuntimeException("键值均不能为空");
        }
        String actualKey = BF_KEY_PREFIX.concat(key);
        boolean result = false;

        try (Jedis jedis = jedisResourcePool.getResource()) {
            // 遍历元素所有hash结果的bit位置
            try (Pipeline pipeline = jedis.pipelined()) {
                for (long index : getBitIndices(element)) {
                    pipeline.getbit(actualKey, index);
                }
                result = !pipeline.syncAndReturnAll().contains(false);
            }
        }
        return result;
    }

    public static void main(String[] args) {
        String path = Path.getCurrentPath() + "/config/zzjodis.properties";
        ConfigReadUtil configReadUtil = new ConfigReadUtil(path);
        try {
            JedisResourcePool jedisResourcePool = RoundRobinJedisPool.
                    create()
                    .curatorClient(configReadUtil.getString("jodisZkStr"), 5000)
                    .zkProxyDir(configReadUtil.getString("zkProxyDir"))
                    .team(configReadUtil.getString("team"))
                    .connectionTimeoutMs(configReadUtil.getInt("connectionTimeoutMs"))
                    .soTimeoutMs(configReadUtil.getInt("soTimeoutMs"))
                    .appKey(configReadUtil.getString("appKey"))
                    .password("".equals(configReadUtil.getString("password")) ? null : configReadUtil.getString("password"))
                    .build();
            BloomFilterUtils bloomFilterUtils = new BloomFilterUtils(10000, 0.01, jedisResourcePool);
            bloomFilterUtils.add("filter01", "element001", 30 * 60);
            System.out.println(bloomFilterUtils.contains("filter01", "element001"));  // true
            System.out.println(bloomFilterUtils.contains("filter01", "element002"));  // false
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

布隆过滤器选择

实现方式储存位置适用场景备注
Guava机器单机只需要机器内存不需要其他资源
Redissonredis分布式连接Redis即可使用
Redis插件redis分布式需要对redis集群进行设置支持布隆过滤器插件
Redis的bitMapredis分布式需要自己实现添加和查询

对于分布式服务环境,Guava方式不适合使用,而Redis插件需要复杂的配置和高成本支持。相比之下,Redisson连接Redis并进行插入和查询的方式更适合当前场景,因此最终选择了Redisson方式。

布隆过滤器初始化问题

例如:600万用户情况下,初始化 数据量和 错误率如何设置?

在 600 万用户 的情况下,布隆过滤器的 初始化数据量(位数组大小)和错误率(误判率) 需要合理设置,以在 存储空间、查询速度和误判率 之间取得平衡。

不同误判率下的计算

误判率 p位数组大小 m每个元素占用位数 m/n最优哈希函数个数 k
1% (0.01)5.16 × 10⁷ (~ 6.45 MB)8.6 bits6.2 ≈ 6
0.1% (0.001)8.59 × 10⁷ (~ 10.74 MB)14.3 bits10
0.01% (0.0001)1.13 × 10⁸ (~ 13.9 MB)18.9 bits13

说明:

  • 误判率越低,所需的位数组就越大。
  • k 取整数,通常取 6~10 之间。

推荐参数

  • 若存储空间有限,查询性能要求高 → 误判率设为 0.01 (1%),约 6.45MB 空间,6 个哈希函数。
  • 若存储空间允许,希望误判率更低 → 误判率设为 0.001 (0.1%),约 10.74MB 空间,10 个哈希函数。

总结

  • 存储空间 m 取 50 100 MB 以内
  • 误判率 p 设定在 0.01 ~ 0.001 之间
  • 哈希函数 k 设定为 6 ~ 10
    这些参数可以在 存储、查询速度和准确率 之间达到良好平衡。
Redis系列--布隆过滤器(Bloom Filter) 无论使用谷歌版本的布隆过滤器还是自己编写的,都会存在两个问题,1、因为不同元素经过hash函数计算后可能会出现相同的hash值(hash碰撞),就会出现一个误判率的问题2、因为有hash碰撞,导致同一个位置可能存放不同的数据,这对于删除操作是很不友好的。对于这些情况可查看另一种布隆过滤器,布谷鸟过滤器。 阅读详情

相关推荐

软件测试——白盒测试实验报告,输入年月日算星期几

软件测试——白盒测试实验报告,输入年月日算星期几,测试用例设计和结果展示

weixin_52278161的博客 2746

布隆过滤器的使用

Redis经常会被问道缓存击穿问题,比较优秀的解决办法是使用布隆过滤器,也有使用空对象解决的,但是最好的办法肯定是布隆过滤器,我们可以通过布隆过滤器来判断元素是否存在,避免缓存和数据库都不存在的数据进行查询访问;也就是说 Guava 的布隆过滤器会根据Funnel 类的定义,计算一个对象的哈希值,放入过滤器。Google 的 Guava 库提供了使用布隆过滤器的 API 类(BloomFilter.class),它是线程安全的。布隆过滤器是用来防止缓存穿透的,我们需要知道如何使用布隆过滤器。

BlueProtocolBlog 1180

TensorRT(C++)部署 Pytorch模型

众所周知,python训练pytorch模型得到.pt模型。但在实际项目应用中,特别是嵌入式端部署时,受限于语言、硬件算力等因素,往往需要优化部署,而tensorRT是最常用的一种方式。本文以yolov5的部署为例,说明模型部署在x86架构上的电脑端的流程。(部署在Arm架构的嵌入式端的流程类似)。

女神sha手的博客 5409

布隆过滤器 使用测试(Google Guava)

布隆过滤器 使用测试Google Guava一、简介二、原理布隆过滤器添加元素布隆过滤器查询元素三、使用Google Guava的BloomFilter添加依赖代码案例测试 当改变预估数时候对真实误判率的影响应用场景 一、简介 布隆过滤器(Bloom Filter),以空间换时间的算法。布隆过滤器由布隆在 1970 年提出。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。 优点 空间效率和查询时间 缺点 有一定的误识别率和删除困难 二、原理 假如有

jj89929665的博客 1468

【时序列】时序列数据如何一步步分解成趋势(trend)季节性(seasonality)和误差(residual)- 详细理解python sm.tsa.seasonal_decompose

【时序列】时序列数据如何一步步分解成趋势(trend)季节性(seasonality)和误差(residual)- 详细理解python sm.tsa.seasonal_decompose 在做时序列分析的时候,好多教程都告诉你要把时序列分解成趋势,季节性,残差,然后画图看一下有没有趋势变化,有没有季节性。像这样: import statsmodels.api as sm decomposition = sm.tsa.seasonal_decompose(train['Count'] ,model='add

weixin_47442572的博客 2万+

反向代理的隐形陷阱:为何你的HTTPS请求会降级为HTTP?

本文深入探讨了Nginx反向代理中HTTPS降级为HTTP的隐形陷阱及其安全风险,分析了五大降级诱因,包括协议信息丢失、重定向配置错误等,并提供了完整的防御性配置方案和实战指南,帮助开发者确保Web应用的安全性。

kotlin6android的博客 453

布隆过滤器讲解及基于Guava BloomFilter案例

布隆过滤器讲解及基于Guava BloomFilter案例

睡竹的博客 2454

simulink中MIL测试简单理解

输入信号设置如下:选中New Test Case1 ---INPUT。5、新建(选择New,Test from Spreadsheet)6、可以新建也可以导入(Use existing test)Source:Output为自己预测结果。3、右键子系统(Create)打开生成表格,可以自己调试。4、创建测试用例表格。保存路径为模型路径。保存路径在模型路径。8、运行测试设h置。

lingdianhuat的博客 1785

玩转Redis-Redis中布隆过滤器的使用及原理

《玩转Redis》系列文章主要讲述Redis的基础及中高级应用。本文是《玩转Redis》系列第【11】篇,本文关键字:玩转Redis、Bloom filter、布隆过滤器、无偏hash函数; - 布隆过滤器的底层原理 - 布隆过滤器的底层结构 - 最佳hash函数数量与错误率的关系 - 所需存储空间与错误率及容量关系 - 布隆过滤器如何扩容 - 布隆过滤器有哪些应用场景 - 布隆过滤器的优缺点 - 延伸拓展

zxiaofan.com 9240

EPICS电机支持(asynMotor)

EPICS电机支持(asynMotor)

yuyuyuliang00的博客 916

《UDS协议从入门到精通》系列——图解0x83:访问时间参数

详细介绍UDS协议0x83服务,目标ECU的时序参数设置有哪些注意点?

车载系统攻城狮 2138

布隆过滤器有什么用?什么原理?如何使用?

布隆过滤器有什么用?什么原理?如何使用?

吴名氏的博客 3万+

air780e stm32标准库发送英文短信,如何实现?(附相关源码,仅供参考)

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 1240

详解各种布隆过滤器原理及使用场景

文章目录1.什么是布隆过滤器?2.布隆过滤器的原理介绍3.布隆过滤器使用场景4.通过 Java 编程手动实现布隆过滤器5.利用Google开源的 Guava中自带的布隆过滤器6.Redis 中的布隆过滤器6.1介绍6.2使用Docker安装6.3常用命令一览6.4实际使用 海量数据处理以及缓存穿透这两个场景让我认识了布隆过滤器 ,我查阅了一些资料来了解它,但是很多现成资料并不满足我的需求,所以就决...

Apple_Boy的博客 2479

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Redis中布隆过滤器的使用及原理

《玩转Redis》系列文章主要讲述Redis的基础及中高级应用。本文是《玩转Redis》系列第【11】篇,最新系列文章请前往公众号“zxiaofan”查看,或百度搜索“玩转Redis zxiaofan”即可。 往期精选:《玩转Redis-HyperLogLog原理探索》 本文关键字:玩转Redis、Bloom filter、布隆过滤器、无偏hash函数; 大纲 布隆过滤器介绍 什么是布隆过滤器 布隆过滤器有什么特性 Redis布隆过滤器实战 rebloom的安装 布隆过滤器的命令详

papaya的博客 2772

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

布隆过滤器原理和使用场景

1.什么是布隆过滤器Bloom Filter 会使用一个较大的 bit 数组来保存所有的数据,数组中的每个元素都只占用 1 bit ,并且每个元素只能是 0 或者 1(代表 false 或者 true),用于检索元素是否存在于大集合中的数据结构。缺点是:有一定的错误识别率2.原理介绍核心原理:数据结构:二进制数组+多个哈希...

计算机毕业论文源码,学生个人网页制作html源码。贴近用户做网络推广和互联网优化。 683

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

JAVA面试题分享五百零九:布隆过滤器有什么用?什么原理?如何使用?

首先,我们需要了解布隆过滤器的概念。布隆过滤器(Bloom Filter,BF)是一个叫做 Bloom 的老哥于 1970 年提出的。我们可以把它看作由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。相比于我们平时常用的 List、Map、Set 等数据结构,它占用空间更少并且效率更高,但是缺点是其返回的结果是概率性的,而不是非常准确的。理论情况下添加到集合中的元素越多,误报的可能性就越大。并且,存放在布隆过滤器的数据不容易删除。

之乎者也·的博客 1167

布隆过滤器的原理、使用场景和注意事项

引题 哈希函数 Bitmap Bitset BloomFilter实现原理 构建布隆的误差率 布隆过滤器实现 JVM实现 guava实现 原理 redis中的布隆过滤器 参考 引题 有50亿个电话号码,现在给你10万个电话号码,如何快速准确的判断出这些号码是否存在? 方案A: DB ? ----> 50亿的电话号码,这查询效率 ? 方案B: 内存 ? —> 就按1个电话号码8个字节 , 50亿*8字节= 40G 内存… 我们业务中

王温暖的博客 931

详解布隆过滤器的原理、使用场景和Java代码示例

今天碰到个业务,他的 Redis 集群有个大 Value 用途是作为布隆过滤器,自己之前只是听说过这个,但是没深入了解过,趁这个机会补充一下知识。 在进入正文之前,之前看到的有句话我觉得说得很好: Data structures are nothing different. They are like the bookshelves of your application where you...

淡然坊 3985

布隆过滤器原理以及java/redis使用

文章目录布隆过滤器布隆过滤器原理使用场景利用Google开源的 Guava中自带的布隆过滤器Redis 中的布隆过滤器使用Docker安装命令 布隆过滤器 布隆过滤器是用于判断一个元素是否在集合中。通过一个位数组和N个hash函数实现。(某样东西一定不存在或者可能存在.) 优点: 空间效率高,所占空间小。 查询时间短。 自带去重 缺点: 元素添加到集合中后,不能被删除。 有一定的误判率 布谷鸟过滤器解决了布隆过滤器无法删除的问题 布隆过滤器原理 当一个元素加入布隆过滤器中的时候,会进行如

NothingToSay 1776

布隆过滤器的基本原理和使用

布隆过滤器的基本原理、误判率公式推导和使用

J_bean的博客 1284

布隆过滤器的原理及使用

布隆过滤器是一种空间效率极高的概率型数据结构,用于快速判断一个元素是否可能存在于一个集合中。它的核心特点是以极小的存储空间换取高效的查询性能,但存在一定的误判率(False Positive)。

qq_42456324的博客 460

详解布隆过滤器的原理、使用场景和注意事项

今天碰到个业务,他的 Redis 集群有个大 Value 用途是作为布隆过滤器,但沟通的时候被小怼了一下,意思大概是 “布隆过滤器原理都不懂,还要我优化?”。技术菜被人怼认了、怪不得别人,自己之前确实只是听说过这个,但是没深入了解过,趁这个机会补充一下知识。 在进入正文之前,之前看到的有句话我觉得说得很好: Data structures are nothing different. T...

旧时光 | YoungChen's 博客 5545

Redis布隆过滤器的安装及使用原理

目录 1.什么是布隆过滤器 2.Redis的布隆过滤器 2.1 安装步骤 2.2 布隆过滤器基本使用 2.3 使用注意事项 前面我们使用HyperLogLog数据结构来进行估数,可以解决很多精确度不高的统计需求。但是如果我们想知道某一个值是否在HyperLogLog里面,它就无能为力了。因为HyperLogLog只提供了pfadd和pfcount方法,没有提供pfcotains这种方法...

Martin的博客 654
上一篇: SpringBoot+AOP+注解实现用户操作日志
下一篇: DeepSeek-R1-Distill-Qwen-7B本地部署方案指南
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值