前言
通过对于位图的认识,位图对于整形的适用比较好,当我们想要进行字符串的映射时,需要将字符串进行转化成整形,但是字符串的来说,当字符串的长度较长时就会显得苍白无力,表示字符的ASCII值有256种,当字符串的长度为10时,字符串的个数就有256的十次方,这个个数是远大于位图的最大空间的,直接地址法直接就失效了。当字符串进行向位图中映射时,由于字符串的数量远大于位图中能够映射的位置,那么必然就会出现一个位置进行有好几个字符串进行映射的结果。
狼多肉少就会出现冲突问题(hash冲突2.0版本)
内容摘要
本文内容包括布隆过滤器的概念、布隆过滤器优化的问题及其底层原理、解释布隆过滤器误判的原因以及优化布隆过滤器的实现、布隆过滤器的解决问题的场景、布隆过滤器的实现思路以及代码展现、探究布隆过滤器的误判率的测试逻辑以及代码展现、布隆过滤器和哈希切分进行配合使用处理海量数据问题等等。
布隆过滤器的概念
布隆过滤器就是为了缓解这种情况进行设计出来的,布隆过滤器并不能解决这种hash冲突2.0版本,只是缓解。本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。
布隆过滤器的原理
布隆过滤器解决哈希冲突2.0版本的逻辑就是,既然一个字符串进行一个映射一个位置容易与别的字符串进行冲突,那我将字符串通过不同哈希函数进行不同的映射是不是就可以明显的降低哈希冲突2.0版本的概率呢??答案确实是这样
通过不同的哈希映射进行映射到相同的位置,只有每个字符串通过不同的哈希函数进行映射到与其他字符串(必须是同一个字符串)同时映射到某几个位置时才发生哈希冲突2.0,当少于哈希函数个数的位置与别的字符串(也是同一个)进行映射到同一个位置不算冲突,例如美团通过某一个哈希函数进行映射后和美团通过某一个哈希函数进行映射后的位置相同,但是他们的另一个映射的位置不相同,这就不是冲突。
当我们进行查找"字节"这个字符串,哈希函数返回了1,5这三个值,然后去1,5这两个位置去查看bit位上存储的值是否为1,如果这两个位置的值都是1,则说明"字节"这个字符串存在。当我们进行查询"百度"这个字符串是否存在时,通过哈希函数返回假如是6,7这两个位置,发现6这个位置bit位存储的值是0,直接说明"百度"这个字符串不存在。
布隆过滤器的缺陷及优化
布隆过滤器的缺陷
布隆过滤器只是能减少字符串的冲突,但是还是会出现误判的情况,如上图所示,当我们要查询"网易"这个字符串是否存在时,经过两个哈希函数的返回,假如是1,2,这时通过查询1,2这两个比特位存储的值全是1,这时就说明"网易"这个字符串存在,这显然是不合理的,所以说布隆过滤器是存在一定的误判的。继续以上图为例,并且当任意一个不存在的字符串讲过哈希函数进行计算,只要哈希函数进行计算的结果不存在6,就一定会将非存在的字符串误判为存在,也就是说当布隆过滤器的长度过短时,进行映射几个值后,所有bit未中的值几乎全部为1,此时在进行查询为未存在的字符串时,几乎都会误判。当布隆过滤器的长度进行变化时,会引起布隆过滤器的误判率。
布隆过滤器是不支持删除操作的,还是如上图所示,当我们进行删除"腾讯"这个字符串时,会将5,7这两个bit位的值变成0,当我们进行查询"字节","美团"这两个字符串时,出现查询不到的结果
说明:布隆过滤器智慧将不存在误判成存在,不会将存在的误判成不存在的。
影响布隆过滤器误判率的因素
通过上面的一些情况可以看出,影响布隆过滤器进行检测字符串是否存在的误判率和布隆过滤器的长度以及哈希函数的个数是密切相关的。
- 在其他因素保持不变的情况下,当布隆过滤器的长度越长时,误判率越低
- 在其他因素保持不变的情况下,当哈希函数的个数越多时,误判率越低
但是也不是说布隆过滤器的长度和哈希函数的个数就是越多越好,当布隆过滤器的长度太长时,会造成内存空间的浪费,当哈希函数的个数越多时,返回的值的个数越多,需要进行查询的bit位越多,效率就会变低。
如何选择布隆过滤器的长度和哈希函数的个数呢??
布隆过滤器的误判率公式:
其中:
- k 是哈希函数的数量。
- n 是要插入的数据个数。
- m 是位数组的大小(即布隆过滤器的长度)。
- e 是自然对数的底数(约为2.71828)。
如何解决布隆过滤器不支持删除的问题???
既然删除字符串会影响其他字符串的查询,如何进行优化解决这个问题呢?通过双位图就可以完美解决。利用另一个位图用于记录在第一个位图中对应位置被映射的次数,当进行某字符串的删除时,利用另一个位图将对应位置的存储数据进行-1处理。
适用场景
能够允许低概率的误判场景
能够起到大量数据的过滤效果,对效率有比较大的提升
举个栗子
解释一下什么叫做允许低概率进行误判的场景,像注册账户,当用户进行填写完想要注册的用户名后,通过布隆过滤器显示用户名已经存在,需要用户重新拟定用户名,这时候通过布隆过滤器进行返回的用户名已经存在,存在小概率的误判可能(用户名没有人注册过,但是布隆过滤器进行误判了),这时候用户自己是不知道这有可能是误判的结果。
但是像电话号码这种就是属于不能允许有误判的可能,当用户输入自己的电话号码,明明这个电话号码没有被注册过,这里却显示被注册了,那么可能这款产品将面临被投诉的可能。这种情况下就是属于不能够允许低概率进行误判的情况,但是通过布隆过滤器在次场景下能够过滤掉电话号码本来就没有注册过的情况,避免了电话号码没有注册过的情况也需要在数据库中进行查找,当手机号判断存在时,这里的存在有可能是因为布隆过滤器误判的情况,所以说还需要在向数据库中进行查询。
布隆过滤器的实现
布隆过滤器也是对于set的复用,布隆过滤器的开辟空间的大小就是布隆过滤器的长度,通过静态常量_x和数据个数N共同控制布隆过滤器的长度,由于字符转整形的通过哈希函数进行返回的值一般都比较大,所以要将他们进行映射到布隆过滤器长度这个空间中,需要进行%一下。
布隆过滤器的测试
布隆过滤器的误判率将分为两种情况进行验证,即相似字符串的误判率和完全不同的字符串的误判率。
- 相似字符串的误判率的测试思路
将原字符串进行加一个字符进行处理,并且字符串不进行重复,使得生成了一个包含原字符串但是和原字符串并不相同的字符串,将大量字符串的数据进行放到vector容器中,然后将vector容器中的数据依次进行映射到布隆过滤器中,在利用上面的思路进行生成一个和上面数据个数相同的字符串,并且和上面生成的大量字符串并不重复,然后将其放到另一个vector容器中,依次取数据和布隆过滤器中的数据进行比对是否存在,如果存在则是误判,进行计数,最后计算误判率即可。
- 不相似字符串的误判率
重新定义一个字符串,使得和原字符串有较大的差异,然后进行包含重新定义的字符串的字符串,进行和布隆过滤器中字符串进行验证。
过程中出现的BUG
哈希切分
给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出 精确算法和近似算法 注:这里的query是查询字符串的意思,这里当成长字符串进行看待即可。
普通切分
假设平均一个query是50字节,那么100亿个query就是500亿个字节换算成内存空间大小有500G,我们只有1G的内存空间,将两个文件中的数据进行放入布隆过滤器不能满足要求,需要将文件进行切分成小文件后再将小文件中的数据放到布隆过滤器中,然后将另一个文件中也进行文件切割,然后将小文件和放入布隆过滤器中的数据进行比对,第二个文件中所有小文件和放入布隆过滤器中的小文件数据进行比对后,再将第一个文件中其余的小文件其中一个继续放到布隆过滤器中,依次进行。
像以上这种比对逻辑,效率十分低,能不能改善上面的比对逻辑将效率进行提高呢??
哈希切分
通过哈希切分将文件进行切分时通过哈希函数计算出对应的hashi,然后将将数据进入对应小文件中,另一个文件也是,然后将相对应位置的小文件进行比对即可,极大提高了比对效率。
哈希切割存在的问题
哈希切割是属于不均等的切割,可能存在某个小文件中的数据过多,小文件过大不能满足内存需求
哈希切割的优化
小文件过大分为两种情况,一种是小文件中数据存在某一个query的大量数据;另一种是小文件中存在大量不同的query。
解决办法:声明一个set/unordered set 进行遍历文件,如果出现抛异常,则说明冲突太多,小文件过大,是属于情况二,换一个哈希函数继续进行切割即可。如果没有抛异常,则说明是情况一,set/unordered set起到了去重的作用。