1.适用场景
在极大的数据集合中快速查找某个元素是否存在,但是不要求100%正确.这个是文绉绉的说法,说人话: 一个很理想的场景:分布式缓存
缓存的数据可能很大,QPS也相当的高,比如千万级,那么一般这个缓存集群就会达到近百台机器的规模.能不能少点机器呢? 毕竟找老板要这么多机器,不容易啊,动不动就是KPI导向!
这个时候Bloom Filter就可以上场了
2.横向对比
为了查找某元素是否出现,一般有哪些方法呢?
- 将所有的数据存起来(比如存数据库),然后直接查询呗: 第一是存不下,第二是找不快
- 使用Hash结构存储:更加存不下,一般hash空间利用率<=50%, 数据量大了,Hash碰撞也不容小觑
- 将原始数据md5或者SHA-1,再存hash:MD5结果128Bit, SHA-1结果160Bit,确实比上面的靠谱多了
- 位向量(将数据哈希值对应的bit位置1):可查证数据显示,要降低冲突发生的概率到1%,就要将BitSet的长度设置为数据总数量的100倍
以上方法都是能百分百准确找到或者找不到元素,空间上多少不尽如人意,但是能百分百准确!可是作为一个缓存服务器,百分百的准确性,有必要么? 为了百分百的准确,多花了多少存储啊!缓存命不中,又不怀孕,是吧? 如果降低5%的准确率,能省20%甚至40%的机器呢?是不是更划算!
3.Bloom Filter原理
布隆过滤器首先是基于上面的方法4,位向量,判断数据的哈希值对应的bit是否为1
改进点在于,使用多个哈希函数,而不是一个
比如数据aaa
index1=func_hash_1(aaa)
index2=func_hash_2(aaa)
那么同时判断bitset[index1]
和bitset[index2]
是否同时为1,就能大致
知道aaa是否存在.
判断规则:
- 只要bitset[index1]活着bitset[index2]任意一个为0,表示数据aaa不存在
- bitset[index1],bitset[index2]全部同时为1,数据aaa
可能
存在
为什么所有的都是1了,只是可能存在呢?仔细想想,每个数据都会被映射到2个标志位,可能刚好index1标志位与bbb的其中一个碰撞(重叠),刚好index2标志位与ccc的其中一个标志位碰撞(重叠).
再直白一点A=func1(aaa)
,B=func2(aaa)
,C=func1(bbb)
,D=func2(bbb)
,AB是数据aaa产生的两个标志位,CD是数据bbb产生的两个标志位.然而很可能aaa
和bbb
原始数据都不存在,而是A=func2(ccc)
,B=func1(ddd)
,C=func2(eee)
,D=func1(fff)
,意思是ccc,ddd,eee,fff分别有一个标志位与aaa,bbb碰撞(重叠),其实aaa,bbb压根不存在
4.Counting Bloom Filter
上面已经举例分析,布隆过滤器存在不准确性,可能误报.误报率是多少,有兴趣额的可以查查,公式就不贴了.
此处另外一个问题,数据一旦写入集合,就不能删除,原因同上面:
ccc,eee,fff,ddd能碰撞虚构出aaa,bbb存在的假象; 那么你删除ccc,ddd,eee,fff时将ABCD标志位置0,也就能虚构aaa,bbb不存在的假象,而此时aaa,bbb可能恰好又倒霉催的真实存在
Counting bloomfilter(CBF),这是一种基本Bloom Filter的变体,CBF将基本Bloom Filter每一个Bit改为一个计数器,出现一个加1,删除一个减1,此时就支持删除数据了
5.数学细节
关于错误率估算
,最优的哈希函数个数
,位数组的大小
,这几个值如何计算调优?鄙人数学不精,不推理公式了,有兴趣的搜索一把,应有尽有