bloomfilter java_Java项目实战篇:用Redis快速实现BloomFilter!

背景

最近工作上有个类似需求是: 现有约3亿条数据词典存在于一个csv文件A中,作为数据源。对于 用户输入的任意单词M,需要快速的在A中匹配M单词是否存在。

(A文件约3G大小左右,总行数三亿)

拿到这个需求,你的第一想法怎么做呢?

正常思路可能是:

将csv文件A导入某关系型数据库。

sql查询按M匹配。

上面的方式有个明显的缺点是:慢!

3亿多行的数据,即便是建好索引进行检索,匹配到也得话不少时间(笔者没亲自试过,感兴趣的朋友可以自行测试测试,理论上快不起来的)。

目前能 在时间复杂度和空间复杂度上达到最佳的方案,恐怕就是Bloom Filter了, 维基地址:Bloom Filter

此处给不太了解Bloom Filter的读者看,熟悉的朋友直接看下一节。

本文场景Bloom Filter 使用思路解释:

假设申请了一段bit位大数组(即数组中的元素只能是一个bit位,1或0,默认元素值都为0)

将csv文件A中的每个单词,经过多个hash函数进行hash运算之后得到在大数组中对应的多个下标位置

将步骤2中得到的多个下标位置的bit位都置为1.

对于用户输入的任意单词M,按照2的步骤得到多个下标位置,其对应大数组中的值全部为1则存在,否则不存在。

方案选型

实现Bloom Filter的方法很多,有各种语言版本的,这里为了真切感受一下算法的魅力,笔者这里决定用java代码徒手撸了!

另一方面,考虑到分布式应用的需要,显然在单机内存上构建 Bloom Filter 存储是不太合适的。 这里选择 redis 。

redis有以下为操作,可以用于实现bloomfilter:

redis>SETBIT bit 10086 1(integer)0redis>GETBIT bit 10086(integer)1redis>GETBIT bit 100# bit 默认被初始化为 0(integer)0Click to copy

实现细节

实现bloom filter的关键是hash函数,一般为了降低误报率、减少hash碰撞的影响,会选择多个hash函数。

那么,怎么写一个hash函数呢?

不要方,我们要的hash是 input: String --> output: int , jdk里面的String类不是恰好也有一个hashCode 方法吗? 翻出来看一看!

/**

* Returns a hash code for this string. The hash code for a

* {@code String} object is computed as

*

 
  

* s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

*

* using {@code int} arithmetic, where {@code s[i]} is the

* ith character of the string, {@code n} is the length of

* the string, and {@code ^} indicates exponentiation.

* (The hash value of the empty string is zero.)

*

* @return  a hash code value for this object.

*/publicinthashCode(){inth=hash;if(h==0&&value.length>0){charval[]=value;for(inti=0;i

看到这一行 h = 31 * h + val[i]; ,貌似原理其实也很简单,每个字符对应的ascii码,经过一个公式计算依次加起来。这里有个系数31 , 稍微变一下, 不就可以有多个hash函数了吗。

以下是稍加修改后的hash函数:

//总的bitmap大小  64Mprivatestaticfinalintcap=1<<29;/*

* 不同哈希函数的种子,一般取质数

* seeds数组共有8个值,则代表采用8种不同的哈希函数

*/privateint[]seeds=newint[]{3,5,7,11,13,31,37,61};privateinthash(String value,intseed){intresult=0;intlength=value.length();for(inti=0;i

剩下的事情便很简单了,对每个词典A中的单词,依次调seeds 中对应的hash函数(这里一共是8个),用redis的setbit操作,将下标值置为1.

redis代码 (这里用pipeline 包装了下。)

@ServicepublicclassRedisService{@AutowiredprivateStringRedisTemplate template;publicvoidmultiSetBit(String name,booleanvalue,long...offsets){template.executePipelined((RedisCallback)connection->{for(longoffset:offsets){connection.setBit(name.getBytes(),offset,value);}returnnull;});}publicListmultiGetBit(String name,long...offsets){Listresults=template.executePipelined((RedisCallback)connection->{for(longoffset:offsets){connection.getBit(name.getBytes(),offset);}returnnull;});Listlist=newArrayList<>();results.forEach(obj->{list.add((Boolean)obj);});returnlist;}}Click to copy

最后,代码串起来大概长这个样子:

FileInputStream inputStream=newFileInputStream("/XXXX.csv");BufferedReader bufferedReader=newBufferedReader(newInputStreamReader(inputStream));HashSettotalSet=newHashSet<>();String word=null;while((word=bufferedReader.readLine())!=null){for(intseed:seeds){inthash=hash(word,seed);totalSet.add((long)hash);}long[]offsets=newlong[totalSet.size()];inti=0;for(Long l:totalSet){offsets[i++]=l;}redisService.multiSetBit("BLOOM_FILTER_WORDS_DICTIONARY",true,offsets);}Click to copy

查的时候也类似:

String word="XXXX";//实际输入long[]offsets=newlong[seeds.length];for(inti=0;iresults=redisService.multiGetBit("BLOOM_FILTER_WORDS_DICTIONARY",offsets);//判断是否都为true (则存在)booleanisExisted=true;for(Boolean result:results){if(!result){isExisted=false;break;}}Click to copy

注意事项

setbit的offset是用大小限制的,在0到 232(最大使用512M内存)之间,即0~4294967296之前,超过这个数会自动将offset转化为0,因此使用的时候一定要注意。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
布隆过滤器(Bloom Filter)是一种重要的数据结构,它用于快速判断一个元素是否存在于一个集合中。布隆过滤器的核心思想是通过一系列哈希函数来对元素进行多次哈希,然后将得到的哈希值映射到一个位数组中,并将对应的位置设为1。当需要判断一个元素是否存在时,同样对其进行多次哈希,检查对应位数组的值是否都为1,若都为1则可以确定元素可能存在;若存在一个0,则可以确定元素一定不存在。因此,布隆过滤器是一种基于概率的数据结构,可以高效地进行查找。 然而,布隆过滤器也存在一些问题。首先,由于多个不同的元素可能会哈希到相同的位上,因此在查询时可能出现误判,即判断一个元素存在时实际上并不存在。这种误判是由于多个元素共享了某一位的原因导致的。其次,布隆过滤器的特性决定了它无法支持元素的删除操作,因为删除一个元素可能会影响其他元素的判断结果,从而增加误判率。 要注意的是,计数布隆过滤器(Counting Bloom Filter)提供了一种实现删除操作的可能性,但并不能保证在后续查询时该值一定返回不存在。因此,不能说计数布隆过滤器支持删除,而是说计数布隆过滤器提供了实现删除的可能。 [3<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【海量数据处理】布隆过滤器BloomFilter](https://blog.csdn.net/qq_43727529/article/details/127180864)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [Java --- redis7之布隆过滤器BloomFilter](https://blog.csdn.net/qq_46093575/article/details/130613434)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值