leveldb研究系列七——FilterPolicy

来源:互联网 发布:爱因斯坦大脑 知乎 编辑:程序博客网 时间:2024/06/13 18:13
FilterPolicy用来过滤不在集合中的key,是用空间换时间的trade 包含下面三个接口:

virtual const char* Name() const = 0; // 返回filter的名字

CreateFilter接口,对key创建filter写入det中

virtual void CreateFilter(const Slice* keys, int n, std::string* dst)const = 0;

KeyMayMatch 接口 匹配过滤key

virtual bool KeyMayMatch(const Slice& key, const Slice& filter)const=0;

对于key的过滤策略采用了Bloom filter.  Bloom filter 是由 Howard Bloom 在 1970 年提出的二进制向量数据结构,它具有很好的空间和时间效率,被用来检测一个元素是不是集合中的一个成员,这种检测只会对在集合内的数据错判,而不会对不是集合内的数据进行错判,这样每个检测请求返回有“在集合内(可能错误)”和“不在集合内(绝对不在集合内)”两种情况,可见 Bloom filter 是牺牲了正确率换取时间和空间.  多个独立的hash函数对key计算填写bit。  在验证的时候计算key的hash指向位是否全部填充。  如果否则key不在集合中,否则则有可能在集合中。


leveldb并没有真正给出多个独立的哈希函数,leveldb选择的策略很简单,通过原始hash,位移产生新的哈希值
Leveldb选择的hash函数是:

h += delta
const uint32_t delta = (h >> 17) | (h << 15);  // Rotate right 17 bits
在bloom_filter的数据的最后一个字节存放的是k_的值,k_实际上就是计算时采用的hash函数个数。下面是

构造函数代码:

 explicit BloomFilterPolicy(int bits_per_key)      : bits_per_key_(bits_per_key) {    // We intentionally round down to reduce probing cost a little bit    k_ = static_cast<size_t>(bits_per_key * 0.69);  // 0.69 =~ ln(2)    if (k_ < 1) k_ = 1;    if (k_ > 30) k_ = 30;  }  virtual const char* Name() const {    return "leveldb.BuiltinBloomFilter";  }
变量k_实际上就是模拟的hash函数的个数,变量bits_per_key_,对于n个key,其hash table的大小就是bits_per_key_。它的值越大,发生冲突的概率就越低,那么bloom hashing误判的概率就越低。因此这是一个时间换空间的策略。

BloomFilter的构建函数如下

 virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const {    // Compute bloom filter size (in both bits and bytes)    size_t bits = n * bits_per_key_;                                   //filter的位长度    // For small n, we can see a very high false positive rate.  Fix it    // by enforcing a minimum bloom filter length.    if (bits < 64) bits = 64;            //长度不能太小 否则容易碰撞    size_t bytes = (bits + 7) / 8;    bits = bytes * 8;                       //取到N个字节数     const size_t init_size = dst->size();    dst->resize(init_size + bytes, 0);              //分配空间    dst->push_back(static_cast<char>(k_));  // Remember # of probes in filter    char* array = &(*dst)[init_size];    for (size_t i = 0; i < n; i++) {      // Use double-hashing to generate a sequence of hash values.      // See analysis in [Kirsch,Mitzenmacher 2006].      uint32_t h = BloomHash(keys[i]);      const uint32_t delta = (h >> 17) | (h << 15);  // Rotate right 17 bits           //模拟产生k个哈希函数      for (size_t j = 0; j < k_; j++) {        const uint32_t bitpos = h % bits;               array[bitpos/8] |= (1 << (bitpos % 8));                // 在bits array上设置第bitpos位          h += delta;      }    }  }
Bloom filter已经构建完成。(哈希函数,再哈希)

BloomFilter的匹配过程相对简单

计算key的hash值,计算全部hash,只要filter上的相应位是0则返回false,匹配结束。对于结果为true表明可能在集合中,如果为false则一定不在集合中.

  virtual bool KeyMayMatch(const Slice& key, const Slice& bloom_filter) const {    const size_t len = bloom_filter.size();    if (len < 2) return false;    const char* array = bloom_filter.data();    const size_t bits = (len - 1) * 8;    // Use the encoded k so that we can read filters generated by    // bloom filters created using different parameters.    const size_t k = array[len-1];    if (k > 30) {      // Reserved for potentially new encodings for short bloom filters.      // Consider it a match.      return true;    }    uint32_t h = BloomHash(key);    const uint32_t delta = (h >> 17) | (h << 15);  // Rotate right 17 bits    for (size_t j = 0; j < k; j++) {      const uint32_t bitpos = h % bits;      if ((array[bitpos/8] & (1 << (bitpos % 8))) == 0) return false;      h += delta;    }    return true;  

到此为止,我们已经分析好leveldb的bloomfilter的过滤策略,这是典型的用时间换空间的买卖。



0 0