BloomFilter
模块信息
util/bloom.cc
模块概要
布隆过滤器由一个很大的bit数组和很多的哈希函数组成,用于判断一个元素是否在集合中。
当要往集合中加入一个元素时,就通过这些哈希函数将这个元素映射到bit数组不同的位上,并将该位设置为1。
当要判断一个元素是否在集合中时,同样通过一系列哈希函数将元素映射到bit数组不同位上,如果出现某个位不为1,则说明该元素不在集合中。
布隆过滤器是有可能产生错误的,为了减少错误,bit数组不能太小,不然很快各个位就变为1了。假设要加入n个元素,有k个哈希函数,bit数组大小位m,则满足以下公式时有最低误差率:
k
=
(
m
/
n
)
∗
l
n
2
k=(m/n)*ln2
k=(m/n)∗ln2
主要接口
方法 | 说明 |
---|---|
void CreateFilter(const Slice* keys, int n, std::string* dst) | 加入n个key到dst中,用string来表示 |
bool KeyMayMatch(const Slice& key, const Slice& bloom_filter) | 判断key是否在集合中 |
源码分析
void CreateFilter(const Slice* keys, int n, std::string* dst) const override {
// 总共的位数为n个key*每个key所占的位数
size_t bits = n * bits_per_key_;
// 为了保证位数不能太少,不然很快就全1了
if (bits < 64) bits = 64;
// 向上找到第一个8的倍数,因为一个字节是8位,为了方便查找
size_t bytes = (bits + 7) / 8;
bits = bytes * 8;
// 获取原有string的大小,resize是调整大小,新增的bytes部分置0,最后一个字节放k
// 可以看出,一个string装了多个bit数组
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
// (*dst)[init_size]是刚刚新增加内容的首个元素,用一个指针array指向它,这样就可以通过
// array来改变string即bit数组的内容了
char* array = &(*dst)[init_size];
for (int i = 0; i < n; i++) {
// 获取元素的哈希值
uint32_t h = BloomHash(keys[i]);
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;
// bitpos/8代表在第几个字节中(char占一个字节),bitpos%8代表在这个字节中的第几位
array[bitpos / 8] |= (1 << (bitpos % 8));
// 每次改变这个哈希值,就像用了k个哈希函数一样
h += delta;
}
}
}
bool KeyMayMatch(const Slice& key, const Slice& bloom_filter) const override {
const size_t len = bloom_filter.size();
if (len < 2) return false;
const char* array = bloom_filter.data();
// len包括了最后一个k的,所以减1
const size_t bits = (len - 1) * 8;
// 获取哈希函数个数
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;
// 哈希映射到的那一位不为1
if ((array[bitpos / 8] & (1 << (bitpos % 8))) == 0) return false;
h += delta;
}
return true;
}
TO_DO
- delta设置为循环右移17位有什么讲究?