从BloomFilter到Counter BloomFilter
文章目錄
- 前言
- 1. Traditional BloomFilter
- 2. Counter BloomFilter
本文traditional bloomfilter 和 counter bloomfilter的C++實現 均已上傳至:
https://github.com/BaronStack/BloomFilter
前言
Bloomfilter 是一個老生常談的數據結構,應用在存儲領域的各個系統之中,用來準確判斷一個字符串是否不存在,高效判斷一個字符串是否存在,有容錯率。
詳細描述和代碼實現均已上傳至github中,本文簡單描述一下原理和關鍵代碼。
1. Traditional BloomFilter
傳統的BloomFilter實現 就是針對輸入的一個字符串列表 作為我們的數據集。
對其中的每一個字符串 通過一個或者多個hash函數生成一個唯一的hash值,將這個值映射到bloomfilter上,把bloomfilter維護的一個bit序列相關的bit位置1。
如下 bloom filter維護的一個bit序列:
第一個string 通過hash 函數 生成的hash值,映射到bloomfilter的三個bit位中,將對應的bit位置1
第二個string 通過同樣的hash函數生成一個唯一的hash值,映射到同一個bloomfilter的bit位中,已經為1的就不用置位了
依此,后續的字符串在構造bloom filter的時候都通過如上方式來構造,這樣一個數據集只需要完成一次構造。
后續拿著構造好的bit序列來檢測輸入的string,只需要通過hash函數拿到hash值就能夠 通過O(1)的時間(位運算)精確匹配這個輸入的string不存在(hash值映射的bit位為0,說明肯定不存在,存在的話之前通過hash值映射的就為1)。
看看如下代碼 構造bloomfilter :
// CreateFilter:根據輸入的key 批量構建bloom filter
// 參數1: keys, 輸入的需要構造成bloomfilter的string列表
// 參數2: n,輸入的元素個數
// 參數3: dst 最終的bloomfilter結果
//
// 這里的構造邏輯是通過雙hash算法實現的,
// 針對輸入的string 先生成一個uint32_t 的hash值
// 再針對其中的num_probe_個bit位進行置1
void BloomFilter::CreateFilter(std::string* keys,int n, std::string *dst ) {size_t bits = n * bits_per_key_; // bits_pter_key_表示需要維護的bloomfilter的位數if(bits < 64) {bits = 64;}size_t bytes = (bits + 7) / 8;bits = bytes * 8;const size_t init_size = dst->size();dst->resize(init_size + bytes, 0);dst->push_back(static_cast<char>(num_probe_)); // Remember # of probeschar* array = &(*dst)[init_size];// Use double-hashing to generate a sequence of hash values.// 這里使用雙hash算法來生成hash值// See analysis in [Kirsch,Mitzenmacher 2006].for(size_t i = 0;i < static_cast<size_t>(n); i++) {uint32_t h = hash_func_(keys[i]);const uint32_t delta = (h >> 17) | (h << 15);for (size_t j = 0; j < num_probe_; j++) {const uint32_t bitpos = h % bits;array[bitpos/8] |= (1 << (bitpos % 8)); // 將bitpos位置置為1h += delta;}}
}
通過如上構造的bloomfilter 來匹配對應的key,輸入的key是否存在于bloomfilter之中
// 檢查輸入的key 是否存在于構造的bloom filter 之中
bool BloomFilter::KeyMayMatch(std::string key, std::string& bloom_filter){const size_t len = bloom_filter.size();if (len < 2) return false;const char* array = bloom_filter.c_str();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 = hash_func_(key);const uint32_t delta = (h >> 17) | (h << 15);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;
}
2. Counter BloomFilter
Counter Blooomfilter的應用場景是 我們想要動態變更構造好的bloomfilter,比如從bloomfilter中刪除一個key的映射。這樣我們之前說的traditional bloomfilter的設計就無法實現了,畢竟bit只有一個值,且可能被多個key的hash映射公用。
這個時候可以維護一個counter(下面的代碼是一個uint32_t的數值表示上面traditional的bit位,當然也可以uint8_t),按照之前的方式來構造,對每一個輸入的key生成一個hash值, 這個hash值映射到bloomfilter之上,讓對應的"bit"位自增即可。刪除其中的key映射的時候,可以讓對應的bit位自減少。
輸入第一個string時保持各個bit位為1
當輸入第二個時,string的hash值映射到同一個bit,則讓bit位數值++即可:
后續的字符串構造過程中類似,也是針對匹配的’bit’位自增即可:
構造過程的實現代碼如下:
// CreateCounterFilter: 創建一個計數功能的bloomfilter
// 參數1:keys ,輸入多個string類型 的字符串
// 參數2: n, 輸入的字符串串長度
// 參數3: 根據輸入的字符串構造出來的計數bloom filter
//
// 原理如下:
// 維護一個uint32_t 的數組,其中每一個元素代表一個bit位
// 0 0 0 0 0 ... 0 0 0
//
// add string1 --> hash_func
// |
// |
// 0 1 0 1 1 ... 1 0 0
//
// add string2 --> hash_func
// |
// |
// 1 2 1 1 2 ... 1 0 1
//
// 如果想要從構造的bloom filter中刪除string1,則只需要讓hash_func
// 的對應 "bit" 位的數值-1 即可
// remove string1 --> hash_func
// |
// |
// 1 1 1 0 1 ... 0 0 1
void CounterFilter::CreateCounterFilter(std::string* keys, int n,vector<uint32_t> *dst) {size_t bits = n* bits_per_key_;// 構造一個存放 uint32_t的bloomfilter數組 -- dstif(bits < 32) {bits = 32;}size_t bytes = (bits + 7) / 8;bits = bytes * 8;const size_t init_size = dst->size();dst->resize(bits, 0);uint32_t* array = &(*dst)[init_size];for(size_t i = 0;i < static_cast<size_t>(n); i++) {uint32_t h = hash_func_(keys[i]);vector<BITTYPE> bit_types;Bitcount(h, &bit_types);for(size_t j = 0; j < bit_types.size(); j++) {if(bit_types[j].is1) {array[bit_types[j].bit_pos] ++;}}}
}
需要注意的是Counter bloomfilter需要針對每一個bit位用更多的字節表示,所以Counter Bloomfilter會消耗更多的內存。
相關代碼完整實現均已上傳至:
https://github.com/BaronStack/BloomFilter
總結
以上是生活随笔為你收集整理的从BloomFilter到Counter BloomFilter的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Rocksdb 写入数据后 GetApp
- 下一篇: 麒麟丸多少钱一盒