BloomFilter布隆過濾器的實現解決緩存擊穿問題

什麼是緩存的擊穿
緩存穿透是指緩存和數據庫中都沒有的數據,緩存每次都無法命中,因爲我們默認不會緩存null 值,導致用戶訪問 id=-1 這樣的數據時,一直都無法命中,這時的用戶很可能是攻擊者,攻擊會導致數據庫壓力過大。
解決方法:
A.在redis緩存null, 當它再次查詢id = -i ,在緩存裏面有值,可以不在訪問數據庫
但是缺點緩存了null 值
B.使用BloomFilter
Bloom Filter是一個佔用空間很小、效率很高的隨機數據結構,它由一個bit數組和一組Hash算法構成。可用於判斷一個元素是否在一個集合中,查詢效率很高(1-N,最優能逼近於1)。
在很多場景下,我們都需要一個能迅速判斷一個元素是否在一個集合中
1.判斷500w條(url)裏面是否包含一個url地址;
2.從數十億個垃圾郵件列表中判斷某郵箱是否垃圾郵箱;
3.將已存在的緩存放到布隆中,當黑客訪問不存在的緩存時迅速返回避免緩存及DB掛掉

用戶訪問,先經過一個集合,集合裏面有所有的數據標識,當集合裏面有標識,代表數據庫裏面也有標識(判斷一個數據在集合裏面有沒有)
a.當集合裏面有,緩存裏面有,返回緩存的值
b.緩存裏面沒有,查詢數據庫。
在這裏插入圖片描述
方案:
1 .使用HashSet 的唯一唯一性,去重
HashSet dataLabel = new HashSet();
dataLabel.add();
dataLable.add();
當數據量很大時,它的內存佔用大不大 ?非常大
一個地址16字節,10億即可達到上百G的內存。HashSet效率逼近O(1),數據庫就不談效率了,不在一個數量級。
2.將地址用MD5算法或其他單向映射算法計算後存入HashSet,無論地址多大,保存的只有MD5後的固定位數
,佔用空間小於存儲完整信息,存在衝突的可能(非垃圾郵箱可能MD5後和某垃圾郵箱一樣,概率低)
3…布隆過濾器,將所有地址經過多個Hash算法,映射到一個bit數組
所有地址經過Hash後映射到同一個bit數組,看清了,只有一個超大的bit數組,保存所有的映射,佔用空間極小,衝突概率高。

大家知道,java中的HashMap有個擴容參數默認是0.75,也就是你想存75個數,至少需要一個100的數組,而且還會有不少的衝突。實際上,Hash的存儲效率是0.5左右,存5個數需要10個的空間。算起來佔用空間還是挺大的。
而布隆過濾器就不用爲每個數都分配空間了,而是直接把所有的數通過算法映射到同一個數組,帶來的問題就是衝突上升,只要概率在可以接受的範圍,用時間換空間,在很多時候是好方案。布隆過濾器需要的空間僅爲HashMap的1/8-1/4之間,而且它不會漏掉任何一個在黑名單的可疑對象,問題只是會誤傷一些非黑名單對象。

BoolmFilter
初始化狀態是一個全爲0的bit數組
數據存儲的容器時bit 集合BitSet
當容量很小,存儲數據很多時,hash 容易碰撞,具體時計算處理的pos 值,一樣了,導致的,通過增加hash 函數的個數來減少碰撞。
在這裏插入圖片描述
如何使用位集合來判斷一個數據是否存在
在這裏插入圖片描述
實現Boolmfilter
public class BloomFilter {

// 1 初始化一個位集合
public static int length = Integer.MAX_VALUE;

public static BitSet sets = new BitSet(length);

// 準備hash  hash
public static int hash_length = 5 ;
public static int []sends = new int[] {17,19,29,31,37};
public static HashFun []funs = new HashFun[hash_length];
static {
	for (int i = 0; i < hash_length; i++) {
		funs[i] = new HashFun(sends[i]);
	}
}

public static void add(String key) {
	for (HashFun hashFun : funs) {
		int hashCode = hashFun.hash(key); // 計算hash 函數
		int pos  = hashCode & (length-1); // 映射在位集合上的位置
		System.out.println("位置有:"+ pos);
		sets.set(pos, true);
	}
}

public static boolean has(String key) {
	for (HashFun hashFun : funs) {
		int hashCode = hashFun.hash(key); // 計算hash 函數
		int pos  = hashCode & (length-1); // 映射在位集合上的位置
		if(!sets.get(pos)) {
			return false ;
		}
	}
	return true;
}
public static void main(String[] args) {
	add("mayun");
	System.out.println("----------------");
    add("mahuateng");
    if(has("mayun")) {
    	System.out.println("ok");
    }else {
    	System.out.println("error");
    }
    
    if(has("mahuateng")) {
    	System.out.println("ok");
    }
}

}

public class HashFun {

private int send ;

public HashFun(int send) {
	this.send = send ;
}
public int hash(String key) {
	int h = 0;

	char []value = key.toCharArray();

	if (h == 0 && value.length > 0) {
		char val[] = value;
		for (int i = 0; i < value.length; i++) {
			h = this.send  * h + val[i];
		}
	}
	return h;
}

}
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章