1.什么是布隆过滤器(Bloom-Filter)
介绍:Bloom-Filter一般用于在大数据量的集合中判定某元素是否存在。
常用应用场景:当并发量很大并且无效请求较多时,很容易造成缓存击穿,使用布隆过滤器来过滤无效请求。
2.布隆过滤器实现原理。
第一步将库中已存在的数据取出;
第二步通过多个相互独立的hash函数将取出的数据进行计算;
第三步将结果映射到bit数组中,第四步输入要过滤的值;
第五步重复第二步中的hash计算;
第六步将结果和bit数组中的值进行比对,假如bit数组中为1的位置hash计算结果为0,则说明数据不存在。
例子:如果库中的值为“123”,hash结果映射到bit数组中的结果是全1。当输入值为“123”,计算hash映射后发现,对应的bit数组中存在0,则说明数据不存在。
优缺点:优点是空间效率和查询时间都远超过一般的算法,缺点是有一定的误识别率和删除困难。布隆过滤器说没有的数据肯定没有,但是返回存在的数据却不一定存在。
3. 布隆过滤器支持对数据的删除吗?
传统的布隆过滤器不支持删除操作。然而其衍生过滤器“Counting Bloom filter”支持元素删除。
4. 如何选择哈希函数个数和布隆过滤器长度
布隆过滤器长度不能过小,当较小时bit数组中的所有位很容易全为1,所有的输入值都会返回数据存在。所以长度不能过小,但过大也会影响查询效率。
哈希函数的个数也需要权衡,个数越多则布隆过滤器 bit 位置位 1 的速度越快,且布隆过滤器的效率越低;但是如果太少的话,那误报率又会变高。
如何选择适合业务的 k 和 m 值呢,常用公式如下:
k 为哈希函数个数,m 为布隆过滤器长度,n 为插入的元素个数,p 为误报率。
6.布隆过滤器的大Value问题
生成环境中建议对体积庞大的布隆过滤器进行拆分。拆分的形式方法多种多样,但是本质是不要将 Hash(Key) 之后的请求分散在多个节点的多个小 bitmap 上,而是应该拆分成多个小 bitmap 之后,对一个 Key 的所有哈希函数都落在这一个小 bitmap 上。
7.布隆过滤器Java实现
import java.util.BitSet;
import java.util.concurrent.atomic.AtomicInteger;
public class BloomFilterPractice {
private final BitSet bitSet;
private final AtomicInteger useCount = new AtomicInteger();
public BloomFilterPractice() {
this( 16);
}
/**
* @param size 过滤器长度
*/
public BloomFilterPractice(int size) {
//每个字符串需要的bit位数*总数据量
long bitSize = size;
if (bitSize < 0 || bitSize > Integer.MAX_VALUE) {
throw new RuntimeException("长度溢出");
}
//创建一个BitSet位集合
bitSet = new BitSet(size);
}
private Double getUseRate() {
return (double) useCount.intValue() / (double) bitSet.length();
}
private int getHashValue(String data, int hashNmber){
int hashCode = data.hashCode();
int hash = hashCode ^ (hashCode >>> 7) ^ (hashCode >>> 4);
hash = hash * hashNmber % bitSet.length();
return Math.abs(hash);
}
public Boolean inputValue(String value){
int[] indexs = new int[16];
int[] hashNumber = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
//假定data已经存在
boolean exist = true;
int index;
for (int i = 0; i < indexs.length; i++) {
//计算位hash值
indexs[i] = index = getHashValue(value, hashNumber[i]);
if (exist) {
//如果某一位bit不存在,则说明该data不存在
if (!bitSet.get(index)) {
exist = false;
setTrue(index);
}
} else {
//如果不存在则直接置为true
setTrue(index);
}
}
return exist;
}
private void setTrue(int index) {
useCount.incrementAndGet();
bitSet.set(index, true);
}
public static void main(String[] args) {
BloomFilterPractice bloomFilterPractice = new BloomFilterPractice();
System.out.println(bloomFilterPractice.inputValue("中国联通"));
System.out.println(bloomFilterPractice.inputValue("中国电信"));
System.out.println(bloomFilterPractice.inputValue("中国移动"));
System.out.println(bloomFilterPractice.inputValue("谁最厉害"));
System.out.println(bloomFilterPractice.inputValue("谁最厉害"));
System.out.println(bloomFilterPractice.getUseRate());
}
}