开发一个电商项目,由于数据量一直在增长(已达亿级),因此须要重构以前开发好的秒杀功能,为了更好的支持高并发,在验证用户是否重复购买的环节,就考虑用布隆过滤器。redis
也顺便更加深刻的去了解下布隆过滤器的原理,感受仍是蛮有意思的,这一连串的公式不静下心来思考,很容易被绕晕。算法
1、概述
一、什么是布隆过滤器
本质上布隆过滤器是一种数据结构,比较巧妙的几率型数据结构
,特色是高效地插入和查询
。根据查询结果能够用来告诉你 某样东西必定不存在或者可能存在
这句话是该算法的核心。数据库
相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,可是缺点是其返回的结果是几率性的,而不是确切的,同时布隆过滤器还有一个缺陷就是网页爬虫
数据只能插入不能删除
。数组
二、数据如何存入布隆过滤器
布隆过滤器是由一个很长的bit数组和一系列哈希函数组成的
。缓存
数组的每一个元素都只占1bit空间,而且每一个元素只能为0或1。服务器
布隆过滤器还拥有k个哈希函数,当一个元素加入布隆过滤器时,会使用k个哈希函数对其进行k次计算,获得k个哈希值,而且根据获得的哈希值,在维数组中把对应下标的值置位1。数据结构
判断某个数是否在布隆过滤器中,就对该元素进行k次哈希计算,获得的值在位数组中判断每一个元素是否都为1,若是每一个元素都为1,就说明这个值在布隆过滤器中。并发
三、布隆过滤器为何会有误判
当插入的元素愈来愈多时,当一个不在布隆过滤器中的元素,通过一样规则的哈希计算以后,获得的值在位数组中查询,有可能这些位置由于其余的元素先被置1了。函数
因此布隆过滤器存在误判的状况,可是若是布隆过滤器判断某个元素不在布隆过滤器中,那么这个值就必定不在。
若是对布隆过滤器的概念还不是很理解的话,推荐一篇博客,图文并茂好理解不少。详解布隆过滤器的原理、使用场景和注意事项
四、使用场景
- 网页爬虫对URL的去重,避免爬去相同的URL地址。
- 垃圾邮件过滤,从数十亿个垃圾邮件列表中判断某邮箱是不是杀垃圾邮箱。
- 解决数据库缓存击穿,黑客攻击服务器时,会构建大量不存在于缓存中的key向服务器发起请求,在数据量足够大的时候,频繁的数据库查询会致使挂机。
- 秒杀系统,查看用户是否重复购买。
2、实际应用场景
背景
如今有个100亿个黑名单网页数据,每一个网页的URL占用64字节。如今想要实现一种网页过滤系统,能够根据网页的URL判断该网站是否在黑名单上,请设计该系统。
需求
能够容许有0.01%如下的判断失误率,而且使用的总空间不要超过200G。
这里一共有4个常量:
100亿条黑名单数据
,每条数据占64个字节
,万分之一的失误率
,总空间不要超过200G
。
若是不考虑不拢过滤器,那么这里存储100亿条数据就须要 100亿 * 64字节 = 596G 显然超过300G
解题
在知足有 100亿条数据 而且容许 万分之一的失误率 的布隆过滤器须要多大的bit数组呢?
- 设bit数组大小为m,样本数量为n,失误率为p。
- 由题可知 n = 100亿,p = 0.01%
布隆过滤器的大小m公式
求得 m = 19.19n,向上取整为 20n。因此2000亿bit,约为186G。
算完m,咱们顺便来算下m,n已知,这时知足最小偏差的k是几个。
哈希函数的个数k公式
求得 k = 14,即须要14个哈希函数。
经过经过 m = 20n, k = 14咱们再来算下真实的失误率。
布隆过滤器真实失误率p公式
求得 p = 0.006%,即布隆过滤器的真实失误率为0.006%。
经过布隆过滤器公式也能够看出:
单个数据的大小不影响布隆过滤器大小,由于样本会经过哈希函数获得输出值
。
就比如上面的 每一个网页的URL占用64字节 这个数据大小 跟布隆过滤器大小没啥关系。
这三个公式就是有关布隆过滤器已经推倒出的公式,下面咱们来推下这个公式是如何推导出来的。
3、公式推导
讲公式,应该先知道几个关键的常量。
误判率p
、布隆过滤器长度m
、元素个数n
、哈希函数个数k
咱们再来一步一步由简单到难推导公式。
一、偏差率公式推导
前提条件
:就是假设每一个元素哈希获得的值分布到m数组上的每个数组节点的几率是相等的。
1) 假设布隆过滤器长度为m,元素个数n为1,哈希函数个数k也为1。那么在插入时某一数组节点没有被置为1的几率。
这个应该很好理解。
2)若是上面其它不变,而哈希函数个数变成k个,那么在插入时某一数组节点没有被置为1的几率。
好理解!
3)若是元素个数变成n个,而哈希函数个数变成k个,那么在插入时某一数组节点没有被置为1的几率。
4)从上面推导出的是: 当布隆过滤器长度为m,元素个数变成n个,哈希函数个数变成k个的时候,某一节点被置为1的几率为
到这里应该也好理解,第三步是该位置从未被置为1,那么1去减去它就是至少有一次被置为1,那么只要存在一次被置1,那么该位置的bit标示就是1,由于布隆过滤器是不能删除的。
5)这个还须要考虑到,一个元素经过hash会生成多个k,放入m数组中,因此须要这k个值都为1才会认为该该元素已经存在。因此是这样的。
上面这个公式推导在转换下就成了
思考
为何上面这个公式的值就是最终的偏差率?
由于当一个布隆过滤器中不存在的元素进来的是的时候,首先经过hash算法产生k个哈希值,分布在m数组上都为1的的几率不就是上面推导出的这个公式吗,那不就是偏差吗?
由于明明是不存在的值,却有这个几率代表已经存在。
思考
给定的m和n,思考k值为多少偏差会最小。
为何k值的大小不合理会影响偏差呢?
咱们来思考下,一个元素最终生成k个hash值,那么会在数组m上的k个位置标记为1。
假设k为1,那么每次进来只在m上的某一个位置标记为1,这样的话若是一个新元素进来恰好hash值也在这里,而不用其它位置来判断是否为1,这个偏差就会比较大。
假设k为m,那么第一个元素进来,在m上全部位置上都表为1了 ,之后只要进来一个元素就会标记为已存在。这个偏差也太大了。
上面只是举了两个极端的例子,但也说明k值太大、过小都很差,它的最优值必定跟m、n存在某种关系。
至于完整公式的推导,我这里就不在写了,后面会贴一我的家怎么推导的博客。
它们之间的关系只要记住下面这个公式就能够了。
这篇博客就到这里了,后面会整理经过谷歌的guava工具 和 redis 实现布隆过滤器的示例。