前言
本文主要描述,使用布隆過濾實現高效快取。文中採用陣列做為快取,如果需要高併發命中,則需將文中的陣列換成Redis資料庫。
布隆過濾
布隆快取的建立過程如下:
1,先定義快取bit陣列(BitArray),陣列的長度就是快取資料的最大數量。
2,然後將字串通過雜湊運算,求出它的HashCode。
3,然後將HashCode作為偽隨機數生成器(Random)的種子,生成一個小於最大數量的正數x。
4,然後將這x作為快取陣列的索引,將陣列[x]的值設定為true。
布隆過濾
程式碼實現
首先建立WinForm專案BloomTest。
然後編寫布隆過濾器,程式碼如下:
public class BloomFilter { //布隆快取陣列 public BitArray BloomCache; //布隆快取陣列的長度 public Int64 BloomCacheLength { get; } public Int64 HashCount { get; } /// <summary> /// /// </summary> /// <param name="bloomCacheLength">布隆快取陣列的長度,預設20000</param> /// <param name="hashCount">hash運算次數,預設3</param> public BloomFilter(int bloomCacheLength = 20000, int hashCount = 3) { BloomCache = new BitArray(bloomCacheLength); BloomCacheLength = bloomCacheLength; HashCount = hashCount; } public void Add(string str) { var hashCode =str.GetHashCode(); Random random = new Random(hashCode); for (int i = 0; i < HashCount; i++) { var x = random.Next((int)(BloomCacheLength - 1)); BloomCache[x] = true; } } public bool IsExist(string str) { var hashCode = str.GetHashCode(); Random random = new Random(hashCode); for (int i = 0; i < HashCount; i++) { if (!BloomCache[random.Next((int)(BloomCacheLength - 1))]) { return false; } } return true; } //錯誤率檢視 public double GetFalsePositiveProbability(double setSize) { // (1 - e^(-k * n / m)) ^ k return Math.Pow((1 - Math.Exp(-HashCount * setSize / BloomCacheLength)), HashCount); } //計算基於布隆過濾器雜湊的最佳數量,即hashCount的最佳值 public int OptimalNumberOfHashes(int setSize) { return (int)Math.Ceiling((BloomCacheLength / setSize) * Math.Log(2.0)); } }
然後編寫布隆過濾器的使用程式碼,如下:
public partial class Form1 : Form { BloomFilter bloom = new BloomFilter(20000, 3); int setSize = 2000; public Form1() { InitializeComponent(); //生成布隆快取陣列 for (int i = 0; i < setSize; i++) { bloom.Add("kiba" + i); } } private void btnSearch_Click(object sender, EventArgs e) { Stopwatch sw = new Stopwatch(); sw.Start(); string con = tbCon.Text.Trim(); var ret = bloom.IsExist(con); sw.Stop(); lblRet.Text = $@"結果:{ret}{Environment.NewLine} 耗時:{sw.ElapsedTicks}{Environment.NewLine} 錯誤概率:{bloom.GetFalsePositiveProbability(setSize)}{Environment.NewLine} 最佳數量:{bloom.OptimalNumberOfHashes(setSize)}"; } }
測試結果
執行專案,點選查詢資料。
如上圖所示,我們成功命中了,如果在專案中,命中了就可以查詢真實快取了。
錯誤概率
布隆快取存在命中錯誤,即如果兩個資料的雜湊運算後值相同,那麼久會存在命中失敗的問題。
錯誤概率可以通過雜湊運算次數和布隆快取陣列長度和插入資料數量計算出來。
最佳數量
布隆快取建議我們多做幾次雜湊運算,即多存幾個快取索引,文中預設建立3個。
我們程式碼中,向布隆快取陣列中插入了2000個資料,通過計算得出,最佳的雜湊運算次數為7,即當插入數量為2000,布隆快取陣列長度為20000時,HashCount的最優值為7。
應用場景
布隆快取有很多場景可以應用,比如重複URL檢測,黑名單驗證,垃圾郵件過濾等等。
舉個例子,爬蟲在爬取網站之前,會先通過布隆過濾計算出該Url是否已經爬取過,再確定是否發起Http請求。
關於快取穿透、快取擊穿、快取雪崩
快取穿透
快取穿透是指快取和資料庫中都沒有的資料,這時使用者不斷的發起這樣的請求,會對資料庫和快取造成比較大的壓力。
解決方案:增加更多,更有效的資料校驗,讓這些請求在進入查詢前被攔截。將快取和資料庫中都沒有的資料寫入快取,並設定一個較短的有效期,用來防止該請求多次進入到資料庫。
快取擊穿
快取擊穿是指快取中的資料正好到期,然後又突然出現大量該資料的訪問。導致大量請求直接傳送到資料庫。
解決方案:對資料進行熱點標記,然後對熱點資料進行特殊有效期設定。對普通資料進行有效期延長處理,比如被請求過一次,加長固定時間的有效期。
快取雪崩
快取雪崩與快取擊穿的意思類似,區別在於,快取擊穿指的是隻有一條資料直接請求資料庫,而雪崩指的是很多這樣的資料直接請求資料庫。
解決方案:快取資料庫分散式部署。
結語
布隆快取因為存在誤判,所以不能用於百分之百定位資料的場景,但如果該場景可以容錯,那布隆快取將大大提升效能。
----------------------------------------------------------------------------------------------------
到此,到此布隆過濾就已經介紹完了。
程式碼已經傳到Github上了,歡迎大家下載。
Github地址: https://github.com/kiba518/BloomFilter_Kiba
----------------------------------------------------------------------------------------------------
注:此文章為原創,任何形式的轉載都請聯絡作者獲得授權並註明出處!
若您覺得這篇文章還不錯,請點選下方的【推薦】,非常感謝!
https://www.cnblogs.com/kiba/p/14767430.html