1.什麼是布隆過濾器?
- 布隆過濾器(Bloom Filter)是一個叫做 Bloom 的老哥於1970年提出的。
- 實際上可以把它看作由二進制向量(或者說位數組)和一系列隨機映射函數(哈希函數)兩部分組成的數據結構。
- 它的優點是空間效率和查詢時間都比一般的算法要好的多,缺點是有一定的誤識別率和刪除困難。
2.布隆過濾器的原理介紹
- 圖解:
-
如圖所示,布隆過濾器添加元素時,該元素首先由多個哈希函數生成不同的哈希值,然後在對應的位數組的下表的元素設置爲 1(當位數組初始化時 ,所有位置均爲0)。當第二次存儲相同字符串時,因爲先前的對應位置已設置爲1,所以很容易知道此值已經存在。
-
如果我們需要判斷某個元素是否在布隆過濾器中時,只需要對給定字符串再次進行相同的哈希計算,得到值之後判斷位數組中的每個元素是否都爲 1,如果值都爲 1,那麼說明這個值在布隆過濾器中,如果存在一個值不爲 1,說明該元素不在布隆過濾器中。
-
不同的字符串可能哈希出來的位置相同,這種情況我們可以適當增加位數組大小或者調整我們的哈希函數。
-
所以,布隆過濾器說某個元素存在,小概率會誤判。布隆過濾器說某個元素不在,那麼這個元素一定不在。
3.布隆過濾器使用場景
- 判斷給定數據是否存在。
- 防止緩存穿透(判斷請求的數據是否有效避免直接繞過緩存請求數據庫)等等、郵箱的垃圾郵件過濾、黑名單功能等等。。
4.用Java 實現布隆過濾器
- 下面是我參考網上已有代碼改的:
/**
* 布隆過濾器
*
* @author wangjie
* @version V1.0
* @date 2020/5/11
*/
public class BloomFilterDemo {
/**
* 位數組的大小
*/
private static int SIZE;
/**
* 通過這個數組可以創建不同的哈希函數
*/
private static int[] SEEDS;
/**
* 位數組。數組中的元素只能是 0 或者 1
*/
private BitSet bits;
/**
* 存放包含 hash 函數的類的數組
*/
private SimpleHash[] func;
/**
* 誤判率
*/
private MisjudgmentRate rate;
/**
* 自動清空
*/
private Double autoClearRate;
/**
* 使用數量
*/
private final AtomicInteger useCount = new AtomicInteger(0);
/**
* 靜態內部類。hash 函數
*/
public static class SimpleHash {
private int cap;
private int seed;
public SimpleHash(int cap, int seed) {
this.cap = cap;
this.seed = seed;
}
/**
* 計算 hash 值
*/
public int hash(Object value) {
int h;
return (value == null) ? 0 : Math.abs(seed * (cap - 1) & ((h = value.hashCode()) ^ (h >>> 16)));
}
}
/**
* 誤判率
*/
public enum MisjudgmentRate {
/**
* 每個字符串分配4個位
*/
VERY_SMALL(new int[] { 2, 3, 5, 7 }),
/**
* 每個字符串分配8個位
*/
SMALL(new int[] { 2, 3, 5, 7, 11, 13, 17, 19 }),
/**
* 每個字符串分配16個位
*/
MIDDLE(new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53 }),
/**
* 每個字符串分配32個位
*/
HIGH(new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
101, 103, 107, 109, 113, 127, 131 });
private int[] seeds;
private MisjudgmentRate(int[] seeds) {
this.seeds = seeds;
}
public int[] getSeeds() {
return seeds;
}
public void setSeeds(int[] seeds) {
this.seeds = seeds;
}
}
/**
* 默認中等程序的誤判率
* @param dataCount 預期處理的數據規模,如預期用於處理1百萬數據的查重,這裏則填寫1000000
*/
public BloomFilterDemo(int dataCount){
this(MisjudgmentRate.MIDDLE, dataCount, null);
}
/**
*
* @param rate 枚舉類型的誤判率
* @param dataCount 預期處理的數據規模,如預期用於處理1百萬數據的查重,這裏則填寫1000000
* @param autoClearRate 自動清空過濾器內部信息的使用比率
*/
public BloomFilterDemo(MisjudgmentRate rate, int dataCount, Double autoClearRate){
long bitSize = rate.seeds.length * dataCount;
if (bitSize < 0 || bitSize > Integer.MAX_VALUE) {
throw new RuntimeException("位數太大溢出了,請降低誤判率或者降低數據大小");
}
this.rate = rate;
SEEDS = rate.seeds;
SIZE = (int) bitSize;
func = new SimpleHash[SEEDS.length];
for (int i = 0; i < SEEDS.length; i++) {
func[i] = new SimpleHash(SIZE, SEEDS[i]);
}
bits = new BitSet(SIZE);
this.autoClearRate = autoClearRate;
}
/**
* 添加元素到位數組
*/
public void add(Object value) {
checkNeedClear();
for (SimpleHash f : func) {
bits.set(f.hash(value), true);
}
}
/**
* 判斷指定元素是否存在於位數組
*/
public boolean contains(Object value) {
boolean ret = true;
for (SimpleHash f : func) {
ret = ret && bits.get(f.hash(value));
}
return ret;
}
/**
* 檢查是否需要清空
*/
private void checkNeedClear() {
if (autoClearRate != null) {
if (getUseRate() >= autoClearRate) {
synchronized (this) {
if (getUseRate() >= autoClearRate) {
bits.clear();
useCount.set(0);
}
}
}
}
}
public double getUseRate() {
return (double) useCount.intValue() / (double) SIZE;
}
public static void main(String[] args) {
String value1 = "fffdfg";
String value2 = "ddbgbfgbfbbgfb";
BloomFilterDemo filter = new BloomFilterDemo(2<<24);
System.out.println(filter.contains(value1));
System.out.println(filter.contains(value2));
filter.add(value1);
filter.add(value2);
System.out.println(filter.contains(value1));
System.out.println(filter.contains(value2));
Integer value11 = 13423;
Integer value21 = 22131;
System.out.println(filter.contains(value11));
System.out.println(filter.contains(value21));
filter.add(value11);
filter.add(value21);
System.out.println(filter.contains(value11));
System.out.println(filter.contains(value21));
}
}
- 運行結果:
false
false
true
true
false
false
true
true