1、什么是布隆过滤器数据库
在数学之美中,有一章是关于布隆过滤器的讲解,内容以下。
网络
在字处理软件中,一个英语单词是否拼写正确;在FBI中,一个嫌疑人的名字是否在嫌疑名单上;在网络爬虫里,一个网址是否已访问过,等等。最直接的方法就是将集合中所有的元素存在计算机中,遇到一个新元素时,将它和集合中的元素之间比较。通常来讲,计算机中的集合是用哈希表存储的。好处是快速准确,缺点是耗费存储空间。当集合很小时,这个问题不明显,当集合规模巨大时,哈希表存储效率低的问题就显现出来了。若是使用哈希表存储Email地址,每一亿个Email地址,就须要1.6GB的内存。为了解决哈希表的这个问题,就须要一种叫布隆过滤器的数学工具。因此,布隆过滤器是个数据工具。他的大小只须要哈希表1/8到1/4的大小就能解决一样的问题。ide
所以,布隆过滤器是一种数学工具。
函数
2、布隆过滤器的原理工具
布隆过滤器其实是一个很长的二进制向量和一系列随机映射函数。咱们经过电子邮件地址的例子来进行说明。
学习
若是须要存储一亿个电子邮件地址,先创建一个16亿二进制(比特),即2亿字节的向量,而后将这16亿个二进制位所有清零。再对每个电子邮件的地址X,用8个不一样的随机数产生器(F1, F2, F3 ..., F8)产生8个信息指纹(f1, f2, f3....., f8)。再用一个随机数产生器G把这8个位置的二进制所有设置为1。对这一亿个电子邮件地址都进行这样的处理后,就产生了一个针对布隆过滤器。
ui
当咱们须要看一个可疑地址Y是否在黑名单中时,用8个相同的随机数产生器(F1, F2, F3, ..., F8)对这个地址产生8个信息指纹s1, s2, s3, ... s8,而后将这8个指纹对应到布隆过滤器的8个二进制位,分别是t1, t2, ...., t8。若是Y在黑名单中,那么t1, t2, ...., t8对应的8个二进制数确定是1。
spa
3、布隆过滤器的优缺点code
优势: 快速,省空间
继承
缺点: 存在必定的误识别率
4、leveldb中的布隆过滤器
由于尚未实际使用过leveldb,因此,我的在这里以为,leveldb的布隆过滤器是在数据库查找时,更快,更省空间。后面具体使用leveldb时,再来理解bloom。下面一块儿来看代码分析
BloomFilterPolicy是继承自FilterPolicy的,关于FilterPolicy在后面的学习中再详述,本节仅讨论Bloom.cc。
1. BloomFilterPolicy类
1.1 BloomFilterPolicy
构造函数,主要是进行初始化,而后肯定须要多少个哈希函数
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; }
1.2 Name
返回bloom过滤器名称
virtual const char* Name() const { return "leveldb.BuiltinBloomFilter2"; }
1.3 CreateFilter
建立BloomFilter,keys是须要存入的key, n是须要存入的个数, dst是BloomFilter的结果
leveldb在最终的BloomFilter上加了一个k_,表示使用了多少个哈希函数,这样在查询时,就能够直接知道用来多少个哈希函数,而不须要从新用一个变量来记录用来多少个哈希函数。
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_; //全部须要建立的key的总位数 // 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; //最小须要64位来保存 //这两行主要进行字节对齐 size_t bytes = (bits + 7) / 8; //对所占的内存进行8字节对齐 bits = bytes * 8; //总共须要的位数 const size_t init_size = dst->size(); dst->resize(init_size + bytes, 0); //将全部的位都置为0 dst->push_back(static_cast<char>(k_)); // Remember # of probes in filter, 将总的哈希函数个数存入最后 char* array = &(*dst)[init_size]; for (int 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 for (size_t j = 0; j < k_; j++) { const uint32_t bitpos = h % bits; //获取第一个数在bits中的位置 // 将数据存入array中 /* bitpos/8计算元素在第几个字节; (1 << (bitpos % 8))计算元素在字节的第几位; 例如: bitpos的值为3, 则元素在第一个字节的第三位上,那么这位上应该赋值为1。 bitpos的值为11,则元素在第二个字节的第三位上,那么这位上应该赋值为1。 为何要用|=运算,由于字节位上的值可能为1,那么新值赋值,还须要保留原来的值。 */ array[bitpos/8] |= (1 << (bitpos % 8)); h += delta; } } }
1.4 KeyMayMatch
查询是否存在函数, key是须要查询的, bloom_filter则是须要使用对比的过滤器
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中bloom的主要代码与分析,能够考虑,之后在本身写代码时,若是存在有大量数据须要查询,读取时,能够先经过布隆过滤器来看是否存在,而后再进行读取。并且布隆过滤器是一种数学方法,从侧面说明了数学与计算机之间的紧密联系,所以,有时间仍是须要对数学进行深刻学习。
更多分享,尽在交流QQ群:199546072