1.概述

转载防丢失,请看原文
算法背景

相似文章:Bing搜索核心技术BitFunnel原理

如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路,存储位置要么是磁盘,要么是内存。很多时候要么是以时间换空间,要么是以空间换时间。

在响应时间要求比较严格的情况下,如果我们存在内里,那么随着集合中元素的增加,我们需要的存储空间越来越大,以及检索的时间越来越长,导致内存开销太大、时间效率变低。

此时需要考虑解决的问题就是,在数据量比较大的情况下,既满足时间要求,又满足空间的要求。即我们需要一个时间和空间消耗都比较小的数据结构和算法。Bloom Filter就是一种解决方案。

Bloom Filter 概念

布隆过滤器(英语:Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

Bloom Filter 原理

布隆过滤器的原理是,当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。

Bloom Filter跟单哈希函数Bit-Map不同之处在于:Bloom Filter使用了k个哈希函数,每个字符串跟k个bit对应。从而降低了冲突的概率。

Bloom Filter的缺点

bloom filter之所以能做到在时间和空间上的效率比较高,是因为牺牲了判断的准确率、删除的便利性

存在误判,可能要查到的元素并没有在容器中,但是hash之后得到的k个位置上值都是1。如果bloom filter中存储的是黑名单,那么可以通过建立一个白名单来存储可能会误判的元素。
删除困难。一个放入容器的元素映射到bit数组的k个位置上是1,删除的时候不能简单的直接置为0,可能会影响其他元素的判断。可以采用Counting Bloom Filter

Bloom Filter 实现

布隆过滤器有许多实现与优化,Guava中就提供了一种Bloom Filter的实现。

在使用bloom filter时,绕不过的两点是预估数据量n以及期望的误判率fpp,

在实现bloom filter时,绕不过的两点就是hash函数的选取以及bit数组的大小。

对于一个确定的场景,我们预估要存的数据量为n,期望的误判率为fpp,然后需要计算我们需要的Bit数组的大小m,以及hash函数的个数k,并选择hash函数

(1)Bit数组大小选择
  根据预估数据量n以及误判率fpp,bit数组大小的m的计算方式:

(2)哈希函数选择
由预估数据量n以及bit数组长度m,可以得到一个hash函数的个数k:

       哈希函数的选择对性能的影响应该是很大的,一个好的哈希函数要能近似等概率的将字符串映射到各个Bit。选择k个不同的哈希函数比较麻烦,一种简单的方法是选择一个哈希函数,然后送入k个不同的参数。

哈希函数个数k、位数组大小m、加入的字符串数量n的关系可以参考Bloom Filters - the math,Bloom_filter-wikipedia

看看Guava中BloomFilter中对于m和k值计算的实现,在com.google.common.hash.BloomFilter类中:

/*** 计算 Bloom Filter的bit位数m** <p>See http://en.wikipedia.org/wiki/Bloom_filter#Probability_of_false_positives for the* formula.** @param n 预期数据量* @param p 误判率 (must be 0 < p < 1)*/
@VisibleForTesting
static long optimalNumOfBits(long n, double p) { if (p == 0) { p = Double.MIN_VALUE; } return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
} /*** 计算最佳k值,即在Bloom过滤器中插入的每个元素的哈希数** <p>See http://en.wikipedia.org/wiki/File:Bloom_filter_fp_probability.svg for the formula.** @param n 预期数据量* @param m bloom filter中总的bit位数 (must be positive)*/
@VisibleForTesting
static int optimalNumOfHashFunctions(long n, long m) { // (m / n) * log(2), but avoid truncation due to division! return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
} 

BloomFilter实现的另一个重点就是怎么利用hash函数把数据映射到bit数组中。Guava的实现是对元素通过MurmurHash3计算hash值,将得到的hash值取高8个字节以及低8个字节进行计算,以得当前元素在bit数组中对应的多个位置。MurmurHash3算法详见:Murmur哈希,于2008年被发明。这个算法hbase,redis,kafka都在使用。

这个过程的实现在两个地方:

将数据放入bloom filter中
判断数据是否已在bloom filter中
这两个地方的实现大同小异,区别只是,前者是put数据,后者是查数据。

这里看一下put的过程,hash策略以MURMUR128_MITZ_64为例:

public <T> boolean put( T object, Funnel<? super T> funnel, int numHashFunctions, LockFreeBitArray bits) { long bitSize = bits.bitSize(); //利用MurmurHash3得到数据的hash值对应的字节数组 byte[] bytes = Hashing.murmur3_128().hashObject(object, funnel).getBytesInternal(); //取低8个字节、高8个字节,转成long类型 long hash1 = lowerEight(bytes); long hash2 = upperEight(bytes); boolean bitsChanged = false; //这里的combinedHash = hash1 + i * hash2 long combinedHash = hash1; //根据combinedHash,得到放入的元素在bit数组中的k个位置,将其置1 for (int i = 0; i < numHashFunctions; i++) { bitsChanged |= bits.set((combinedHash & Long.MAX_VALUE) % bitSize); combinedHash += hash2; } return bitsChanged;
}

判断元素是否在bloom filter中的方法mightContain与上面的实现基本一致,不再赘述。

Bloom Filter的使用

简单写个demo,用法很简单,类似HashMap

package com.qunar.sage.wang.common.bloom.filter; import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnel;
import com.google.common.hash.Funnels;
import com.google.common.hash.PrimitiveSink;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.ToString; /*** BloomFilterTest** @author sage.wang* @date 18-5-14 下午5:02*/
public class BloomFilterTest { public static void main(String[] args) { long expectedInsertions = 10000000; double fpp = 0.00001; BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), expectedInsertions, fpp); bloomFilter.put("aaa"); bloomFilter.put("bbb"); boolean containsString = bloomFilter.mightContain("aaa"); System.out.println(containsString); BloomFilter<Email> emailBloomFilter = BloomFilter .create((Funnel<Email>) (from, into) -> into.putString(from.getDomain(), Charsets.UTF_8), expectedInsertions, fpp); emailBloomFilter.put(new Email("sage.wang", "quanr.com")); boolean containsEmail = emailBloomFilter.mightContain(new Email("sage.wangaaa", "quanr.com")); System.out.println(containsEmail); } @Data @Builder @ToString @AllArgsConstructor public static class Email { private String userName; private String domain; } } 

Bloom Filter的应用

常见的几个应用场景:

cerberus在收集监控数据的时候, 有的系统的监控项量会很大, 需要检查一个监控项的名字是否已经被记录到db过了, 如果没有的话就需要写入db.
爬虫过滤已抓到的url就不再抓,可用bloom filter过滤
垃圾邮件过滤。如果用哈希表,每存储一亿个 email地址,就需要 1.6GB的内存(用哈希表实现的具体办法是将每一个 email地址对应成一个八字节的信息指纹,然后将这些信息指纹存入哈希表,由于哈希表的存储效率一般只有 50%,因此一个 email地址需要占用十六个字节。一亿个地址大约要 1.6GB,即十六亿字节的内存)。因此存贮几十亿个邮件地址可能需要上百 GB的内存。而Bloom Filter只需要哈希表 1/8到 1/4 的大小就能解决同样的问题。

好文章:
https://blog.csdn.net/jiaomeng/article/details/1495500
https://blog.csdn.net/he_ranly/article/details/94433004

【guava】大数据量下的集合过滤—Bloom Filter相关推荐

  1. 大数据量下的集合过滤—Bloom Filter

    算法背景 如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定.链表.树.散列表(又叫哈希表,Hash table)等等数据结构都是这种思路,存储位置要么是磁盘 ...

  2. 一招教你解决大数据量下的各种报表使用问题

    在我们日常制作报表分析过程中,总会遇到各种问题.比如,报表底层数据日益增多.报表加载超慢,这些情况该怎么解决? 数据库是最常见的能处理大数据的计算方案,而永洪能利用数据库来完成数据计算.但是,有些报表 ...

  3. 大数据量下,身份证的查询优化

    大数据量下,身份证的查询优化 这里是测试练习采用select *,实际场景中还是使用所有字段的形式,这样也可以提高效率 方式一:身份证分别正向.逆向存储,使用like逆序模糊查询,满足最左匹配原则,索 ...

  4. Java8 Stream 数据流,大数据量下的性能效率怎么样?

    今日推荐程序猿惯用口头禅,你被击中了吗? 常见代码重构技巧(非常实用) B站,牛啊. 程序员缺乏经验的 7 种表现 2021年4月程序员工资统计:平均14596元,南京程序员收入挤进一线. 来源:bl ...

  5. java导出Excel增加下拉框选项,解决小数据量和大数据量下拉框选项的问题

    文章目录 java导出Excel增加下拉框选项 一.小数据量情况 二.大数据量情况 java导出Excel增加下拉框选项(java结合easyExcel) 添加传参模型ConsumablesAddDT ...

  6. 大数据量下的sort

    http://blog.chinaunix.net/uid-23586172-id-3349317.html sort在linux命令行下面是一个非常好用的工具,有人把它当做每个程序员都应该知道的8个 ...

  7. 大数据量下高并发同步的讲解(不看,保证你后悔!)

    偶然的机会在网上看到了这篇blog,觉得作者写得挺不错的(虽然自己并没有怎么看懂...),所以就转来跟大家分享分享吧~~~ 对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发 ...

  8. 大数据量下水晶报表的实现及显示过程中的进度条显示讨论

    最近一段收到的反馈中,有几位是问到在应用程序中使用水晶报表时,大数据量情况下因为等待时间过长,给用户的感觉不好 所以想增加一个进度条,给用户一个比较直观的印象. 本文针对此问题而生,但是并没有一个像样 ...

  9. 大数据量下高并发同步的讲解(不看,保证你后悔)

    对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了.而并发问题是绝大部分的程序员头疼的问题, 但话又说回来了,既然逃避不掉,那我们就坦然面对吧~今天就让我们一起来研 ...

最新文章

  1. 【图论专题】单源最短路的扩展应用
  2. 顺序表-顺序表表示集合-并集(A复制到C,B遍历比较C)
  3. 用远线程实现文件自删除
  4. C++ 的复制构造函数
  5. android+5.0+小米手环,小米手环5和荣耀手环6哪个好-参数对比
  6. iOS11最新隐私信息访问列表
  7. appian php,aha2mao
  8. 值得收藏的30道Python练手题(附详细答案)
  9. 免费推广网站实例(珍藏版)
  10. 网络安全问题及防护措施有哪些
  11. 阿里矢量图标引入方法
  12. 冰点文库下载器的使用
  13. w10系统老是自动息屏_win10关闭屏幕后黑屏无法唤醒?你一定要学会这几招解决方法...
  14. 搭建内网DNS服务器教程
  15. 去跨国公司还是去创业公司?
  16. 游戏开发相关书籍推荐,简介
  17. 酒馆指南:小白开店必备
  18. Java常用类字符串异常处理
  19. Python 数据库连接方法和数据库连接池
  20. oss连接的简单实现与文件的简单获取

热门文章

  1. 疯狂的华为MateX2:375万人在线抢,转手一台赚2万
  2. 跟谁学宣布独立调查结论:没有造假 盘后股价涨超8%
  3. 热情不减!iPhone12国行首批供货已售罄
  4. 苹果iPhone 12系列智能手机支持北斗卫星导航定位
  5. 修手机时创意被剽窃,男子向苹果索赔7万亿!是认真的吗?
  6. 特斯拉加州工厂无视禁令强行复工,马斯克:要抓就只抓我
  7. “浴霸”三摄出镜率真高!中兴Blade 20工信部入网
  8. 在线打假!“鲁迅说过的话”检索系统上线 网友太热情系统一度崩溃
  9. OPPO Reno配置曝光 骁龙855+10倍混合光学变焦
  10. 阿里影业正式成为阿里集团子公司 俞永福辞任执行董事