布隆過濾器解決"面試題:
- 如何建立一個十億級別的哈希表,限制內存空間"
- "如何快速查詢一個10億大小的集合中的元素是否存在"
如題
布隆過濾器確實很神奇, 簡單來說就是通過多次hash將key存進一個集合中,可以灰常快速地在數億級的數據中快速查找!
實現布隆過濾器需要用bit位存儲的數組, 千萬別用int[] ,畢竟一個int整形占32位,一個int = 32 bit!
但是Java沒有bit, 那用byte吧,一個byte(8位)當做8位的bit來算吧,每一位代表一個具體的值來進行hash; 解析hash和設置hsah值的時候, 需要位運算提取出每位上的值(每位上的0或1)!
但是java的byte還需要分正負,默認一個byte的范圍為[-128,127] !
部分學習借鑒搬運的博客原文鏈接
https://www.cnblogs.com/liyulong1982/p/6013002.html
https://baike.baidu.com/item/布隆過濾器/5384697?fr=aladdin
筆記整理
算法核心
- 首先需要k個hash函數,每個函數可以把key散列成為1個整數
- 初始化時,需要一個長度為n比特的數組,每個比特位初始化為0
- 某個key加入集合時,用k個hash函數計算出k個散列值,並把數組中對應的比特位置為1
- 判斷某個key是否在集合時,用k個hash函數計算出k個散列值,並查詢數組中對應的比特位,如果所有的比特位都是1,認為在集合中。
優點:
不需要存儲key,使用節省空間
缺點:
- 算法判斷key在集合中時,有一定(通過優化算法可以降到很低)的概率key其實不在集合中
- 無法刪除
典型的應用場景:
- 某些存儲系統的設計中,會存在空查詢缺陷:當查詢一個不存在的key時,需要訪問慢設備,導致效率低下。
- 比如一個前端頁面的緩存系統,可能這樣設計:先查詢某個頁面在本地是否存在,如果存在就直接返回,如果不存在,就從后端獲取。但是當頻繁從緩存系統查詢一個頁面時,緩存系統將會頻繁請求后端,把壓力導入后端。
- 這是只要增加一個bloom算法的服務,后端插入一個key時,在這個服務中設置一次
需要查詢后端時,先判斷key在后端是否存在,這樣就能避免后端的壓力. - 最近還學到, 布隆過濾器還可以防止緩存雪崩, 原理同上.
黑客通過大量請求數據庫中不存在的key, 導致遍歷整個緩存和數據路進行查詢, 每次都無法讓前端的緩存發揮效果,緩存系統將會頻繁請求后端數據庫,很快就會造成系統雪崩.
因此可以利用布隆過濾器進行解決這個問題.
- 解決思路: 將數據庫中的key全部建立到布隆過濾器中, 每次請求先查詢布隆過濾器; 如果存在,則放行, 畢竟布隆過濾器會有很少部分key會誤算!
- 注意: 通過布隆過濾器的值,極大地概率存在着這個key;不通過的key, 那么一定不存在.
模擬布隆過濾器; 先挖坑,扔在這里
一個byte分為8位用,故1250萬的byte數組就可以了.
該數組大小為: .
package com.szs.test;
public class myBloomFilter {
//一個boolean只占一個字節, 一個字節是八位,把每個字節拆成八位來用
//一個byte分為8位用,故可哈希出10億個具體的數據,1.250億的byte數組就可以了,但是需要125M更多的內存.
// 但是具體題目一般都有內存的限制(比如100M 以內), 還有其他的情況需要考慮!
//10^9 == 10億 , 故int就夠了.( int的取值范圍為: -2^31——2^31-1,即-2147483648——2147483647 , 大概最高值為2*10^9)
//如下數組為1.25*10^7大小.
private static byte[] array01 = new byte[12500000];
private static byte[] array02 = new byte[12500000];
private static byte[] array03 = new byte[12500000];
/** 簡單的布隆過濾器的main測試類
* @param args
*/
public static void main(String[] args) {
}
/**
* 查找一個key,判斷是否存在;若存在返回true,否則返回false
* @param x
*/
public static boolean findKey(long x) {
return true;
}
/**
*
* @param x
*/
public static void insertKey(long x) {
}
/**
* 刪除一個key, 暴力for循環進行刪除
* @param x
*/
public static void deleteKey(long x) {
}
/**
* 嘗試hash后,返回對應的hash值,
*/
public static void hashKeyThreeTimes(long x) {
}
/**
* 存儲hash值到數組中的index下標
* @param x
*/
public static void storeHashCode(long hashCode) {
}
}
其他思路
- 量子計算, 一個量子有八種狀態,其實就是八進制,兩個量子就可以枚舉64種狀態; 依次類推,十億級別數據量的時間復雜度為: log810^9= 9 .故時間復雜度為O(1).
- 並行計算: 多處理進行處理.
- 增加系統內存,增加JVM虛擬機的可分配內存.
