Cassandra中布隆过滤器详解

CassandraBloomFIlter实现详解

零、BloomFilter原理概述

http://hi.baidu.com/waxiga/blog/item/33ef2ff49b138530bd3109ad.html

http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.htmlcassandra中用到了其中的结论,特别注意那个表格)

一、从getFilter()函数入手

1.1第一个getFilter()函数传入参数为元素的个数numElements、期望每个元素的桶个数targetBucketsPerElem(m/nm为比特数组位数,n为元素个数)。核心代码:

public static BloomFilter getFilter(long numElements, int targetBucketsPerElem)
{
		int maxBucketsPerElement = Math.max(1, maxBucketsPerElement(numElements));
		int bucketsPerElement = Math.min(targetBucketsPerElem, maxBucketsPerElement);
		BloomCalculations.BloomSpecification spec = BloomCalculations.computeBloomSpec(bucketsPerElement);
		return new BloomFilter(spec.K, bucketsFor(numElements, spec.bucketsPerElement));
}

1maxBucketsPerElementnumElements)函数返回每个元素最大的桶数目。返回值为:min(BloomCalculations.probs.length- 1, Integer.MAX_VALUE- 20)/ (double)numElements);其中前一项值为15.maxBucketsPerElement变量最大值为15

2)实际每个元素的桶数目为min(targetBucketsPerElem,maxBucketsPerElement);即取期望的桶数目和实际可能最大的桶数目中间的小值。

3)根据实际每个元素的桶数目,选择哈希函数的个数。BloomCalculations.BloomSpecificationspec =BloomCalculations.computeBloomSpec(bucketsPerElement);其中computeBloomSpecbucketsPerElement)函数主要是创建BloomSpecification对象,newBloomSpecification(optKPerBuckets[bucketsPerElement],bucketsPerElement);其中构造函数参数为在当前bucketsPerElement的情况下,误报率最低的哈希函数的个数(即最优的哈希函数的个数optK)。

4)创建BloomFilter对象并返回。参数为哈希函数个数spec.K,以及BitSet对象(由bucketsFor()函数返回。该函数主要是根据元素数目和每个元素的桶数目,创建具有min(Integer.MAX,numElements* bucketsPer + 20)大小的BitSet)。


1.2第二个getFilter()函数:传入参数是元素个数和错误率。

public static BloomFilter getFilter(long numElements, double maxFalsePosProbability)
{
	assert maxFalsePosProbability <= 1.0 : "Invalid probability";
	int bucketsPerElement = maxBucketsPerElement(numElements);
	BloomCalculations.BloomSpecification spec = BloomCalculations.computeBloomSpec(bucketsPerElement, maxFalsePosProbability);
	return new BloomFilter(spec.K, bucketsFor(numElements, spec.bucketsPerElement));
}


1)同上,maxBucketsPerElementnumElements)函数返回每个元素最大的桶数目。返回值为:min(BloomCalculations.probs.length- 1, Integer.MAX_VALUE- 20)/ (double)numElements);其中前一项值为15.maxBucketsPerElement变量最大值为15

2根据每个元素的最大的桶数目和错误率,创建BitSet对象。

3)主要函数BloomCalculations.computeBloomSpec(bucketsPerElement,maxFalsePosProbability);

主要作用就是求出满足错误率要求的最小的butcktsPerElement和最小的哈希函数个数K

4)最后构造BloomFilter对象返回,跟前一个一样。

可以总结下,maxBucketsPerElement最大是15,故哈希函数个数最多为8个。


二、BloomFilter


addStringkey)函数分析

1)创建了BloomFilter对象后,接下来分析其中的函数,调用add函数将相应的值加入布隆过滤器,即对其经过哈希后相应的BitSet位置位。

public void add(String key){
	for (int bucketIndex : getHashBuckets(key))
	{
		filter_.set(bucketIndex);
	}
}

getHashBucketskey)函数返回key经过哈希后需要置位的int类型数组,filter_即为创建的BitSet对象。然后调用filter_.set(bucketIndex)BitSet相应的位置位。


2getHashBuckets(key)->Filter.getHashBuckets(key)->Filter.getHashBuckets(key,hashCount,buckets())->Filter.getHashBuckets(byte[]b, inthashCount, intmax)

其中buckets()函数即返回filter_.size()一个小点注意下:BItSet构造函数创建对象时,如果你指定的大小不是字对齐的,则创建后的大小会自动对其。比如你指定大小为100,实际创建的大小就是128


3)关键的哈希函数就是Filter.getHashBuckets(byte[]b, inthashCount, intmax)

static int[] getHashBuckets(byte[] b, int hashCount, int max)
{
		int[] result = new int[hashCount];
		int hash1 = hasher.hash(b, b.length, 0);
		int hash2 = hasher.hash(b, b.length, hash1);
		for (int i = 0; i < hashCount; i++)
		{
			result[i] = Math.abs((hash1 + i * hash2) % max);
		}
		return result;
}


从代码中可以看出,这个哈希函数用到了双重散列,我们知道在所有的开放寻址法中,双重散列是最好方法之一。因为双重散列用到了O(m^2)种探查序列。具体分析可参见算法导论11.4节。


哈希函数用的是MurmurHash对象的hash函数,该函数很复杂,就不分析了。代码如下:

public int hash(byte[] data, int length, int seed) {
    int m = 0x5bd1e995;
    int r = 24;

    int h = seed ^ length;

    int len_4 = length >> 2;

    for (int i = 0; i < len_4; i++) {
      int i_4 = i << 2;
      int k = data[i_4 + 3];
      k = k << 8;
      k = k | (data[i_4 + 2] & 0xff);
      k = k << 8;
      k = k | (data[i_4 + 1] & 0xff);
      k = k << 8;
      k = k | (data[i_4 + 0] & 0xff);
      k *= m;
      k ^= k >>> r;
      k *= m;
      h *= m;
      h ^= k;
    }

    // avoid calculating modulo
    int len_m = len_4 << 2;
    int left = length - len_m;

    if (left != 0) {
      if (left >= 3) {
        h ^= (int) data[length - 3] << 16;
      }
      if (left >= 2) {
        h ^= (int) data[length - 2] << 8;
      }
      if (left >= 1) {
        h ^= (int) data[length - 1];
      }

      h *= m;
    }

    h ^= h >>> 13;
    h *= m;
    h ^= h >>> 15;

    return h;
  }


isPresent(Stringkey)函数分析

1)函数代码如下:

public boolean isPresent(String key)
{
		for (int bucketIndex : getHashBuckets(key))
		{
			if (!filter_.get(bucketIndex))
			{
				return false;
			}
		}
		return true;
}


2)由add函数分析就很容易了,根据key值哈希后得到的数组,判断数组中的值是否置位,只有所有的位都置位了才可能在里面,只要有一位没有置位,则key值肯定不在里面。




<think>嗯,用户之前让我介绍了LSTM的推导公式,现在又转向了布隆过滤器的原理和缺点。看来用户可能在学习或工作中需要了解不同的数据结构或算法,尤其是那些用于处理大数据或概率型数据结构的。 首先,我需要确认用户对布隆过滤器的了解程度。因为用户之前问的是LSTM,可能有一定的机器学习或深度学习背景,但布隆过滤器更多是计算机科学中的数据结构,用于高效查询和空间节省。所以用户可能是在扩展知识面,或者遇到了需要快速判断元素是否存在但可以接受一定误判的场景。 接下来,我要解释布隆过滤器的原理。记得布隆过滤器是一个空间效率很高的概率型数据结构,用来判断一个元素是否在一个集合中。但可能存在误判,即判断某个元素可能存在而实际不存在,但不会漏判,即如果判断不存在则一定不存在。 然后,需要分步骤说明原理。首先是初始化,一个位数组和多个哈希函数。添加元素时,用哈希函数映射到位数组的多个位置,置为1。查询时,检查所有对应位是否都为1,如果都是,则认为可能存在,否则肯定不存在。 接下来是优点,比如空间效率高,查询时间快,但必须提到缺点。用户明确问到了缺点,所以需要详细说明。缺点主要有误判率、无法删除元素、哈希函数的选择影响性能等。 可能用户的实际应用场景是什么呢?比如在数据库查询前做快速过滤,减少不必要的磁盘访问,或者网络中的缓存击穿保护。这时候,用户可能需要权衡误判率和空间效率,以及是否支持删除操作,这时候可能需要提到布隆过滤器的变种,比如计数布隆过滤器,以支持删除。 另外,用户可能没有提到的但需要了解的是,如何根据预期元素数量和可接受的误判率来计算布隆过滤器的大小和哈希函数的数量。这部分虽然用户没问,但可能在解释原理时补充说明会更全面,帮助用户理解参数设置的重要性。 需要确保语言通俗易懂,避免过多数学公式,但必要时给出关键公式,比如误判率的计算公式,或者参数之间的关系,比如位数组大小m、哈希函数数量k和元素数量n之间的关系,m = - (n * ln(p)) / (ln2)^2,其中p是误判率。 同时,用户可能关心如何选择哈希函数,是否需要独立性等,这也是影响布隆过滤器性能的因素之一。另外,要强调布隆过滤器适用于容忍一定误判但对空间和查询时间有高要求的场景,不适合必须100%准确的场景。 最后,总结优缺点,并可能提到替代方案,比如布谷鸟过滤器,它解决了删除的问题,但可能有其他权衡。这样用户可以有更全面的了解,并根据实际需求选择合适的数据结构。</think>### **布隆过滤器(Bloom Filter)原理** #### **1. 核心思想** 布隆过滤器是一种**空间高效的概率型数据结构**,用于快速判断一个元素是否属于某个集合。其特点: - **可能存在误判**(False Positive),但**不会漏判**(False Negative)。 - **牺牲一定准确性**,换取极低的内存占用和常数时间的查询效率。 --- #### **2. 实现原理** ##### **(1) 初始化** - 创建一个长度为 \( m \) 的**二进制位数组**(初始全为0)。 - 选择 \( k \) 个独立的**哈希函数**(每个函数将元素映射到 \( [0, m-1] \) 的某个位置)。 ##### **(2) 添加元素** - 对元素 \( x \) 依次用 \( k \) 个哈希函数计算,得到 \( k \) 个位置 \( h_1(x), h_2(x), ..., h_k(x) \)。 - 将位数组中这 \( k \) 个位置的值置为1。 ##### **(3) 查询元素** - 对元素 \( y \) 同样用 \( k \) 个哈希函数计算,得到 \( k \) 个位置。 - **若所有位置的值均为1** → 认为 \( y \) 可能存在于集合中(存在误判可能)。 - **若至少一个位置为0** → \( y \) 一定不存在于集合中。 --- ### **3. 核心公式** - **误判率(False Positive Probability)**: \[ P \approx \left(1 - e^{-kn/m}\right)^k \] 其中: - \( n \): 已插入元素数量 - \( m \): 位数组长度 - \( k \): 哈希函数个数 - **最优哈希函数数量**: \[ k = \frac{m}{n} \ln 2 \] 此时误判率最低。 --- ### **4. 优点** 1. **空间效率极高**:仅需存储二进制位,远低于哈希表。 - 例如:存储1亿元素,误判率1%时,仅需约 114MB(传统哈希表需数GB)。 2. **查询时间复杂度为 \( O(k) \)**:与数据规模无关。 3. **并行化友好**:哈希函数可独立计算。 --- ### **5. 缺点** #### **(1) 误判率不可避免** - 随着元素增加,误判率逐渐上升。 - 直接删除(将位由1置0)可能导致其他元素的哈希位被误删。 - 改进方案:使用**计数布隆过滤器**(以计数器代替二进制位,但占用更多空间)。 #### **(3) 参数敏感** - 性能高度依赖 \( m, k \) 的选择:需预先估计元素数量 \( n \),否则误判率可能失控。 #### **(4) 哈希函数要求高** - 需要多个独立、均匀分布的哈希函数,设计不当会导致冲突率激增。 --- ### **6. 应用场景** - **缓存系统**:快速判断请求数据是否在缓存中,避免穿透到数据库。 - **爬虫去重**:过滤已抓取的URL。 - **分布式系统**:如HBase、Cassandra中快速判断键是否存在。 - **网络路由**:拦截垃圾邮件或恶意请求。 --- ### **7. 变种改进** | 类型 | 改进点 | 缺点 | |--|--------------------------| | 计数布隆过滤器 | 支持删除操作 | 内存占用增加 | | 布谷鸟过滤器(Cuckoo Filter)| 支持删除、更低误判率 | 实现复杂 | | 动态布隆过滤器 | 支持动态扩容 | 扩容时性能下降 | --- ### **总结** 布隆过滤器通过**概率化**和**空间压缩**,在特定场景下实现高效过滤,但其误判率和功能局限性需在设计中权衡。适合对空间敏感、允许少量误判的“防御性查询”场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值