1、布隆過濾器是什么?
又快又小的處理方法
布隆過濾器(Bloom Filter):是一種空間效率極高的概率型算法和數據結構,用於判斷一個元素是否在集合中(類似Hashset)。
它的核心一個很長的二進制向量和一系列hash函數
數組長度以及hash函數的個數都是動態確定的。
Hash函數:SHA1,SHA256,MD5..
2、應用的經典場景
一個像Yahoo,HotMail和Gmail那樣的公眾電子郵件提供商,
總是需要過濾來自發送垃圾郵件的人的垃圾郵件,
一個辦法就是記錄下那些發送垃圾郵件的email地址,
由於那些發送者不停地再注冊新的地址,全世界少說也有五十億個發垃圾郵件的地址,
怎么樣迅速的判斷一個郵件地址是不是垃圾郵件地址?把它存起來然后確認?
一個郵箱平均18個字節,50億個郵箱容量多大?
18byte x 50億 = 90億
3、優勢和劣勢
優勢:
全量存儲但是不存儲元素本身,在某些對保密要求非常嚴格的場合有優勢;
空間高效率
插入/查詢時間都是常數O(k),遠遠超過一般的算法
劣勢:
存在誤算率(False Positive),隨着存入的元素數量增加,誤算率隨之增加;
一般情況下不能從布隆過濾器中刪除元素;
數組長度以及hash函數個數確定過程復雜;
4、應用場景
- Google著名的分布式數據庫Bigtable以及Hbase使用了布隆過濾器來查找不存在的行或列,以及減少磁盤查找的IO次數
- 文檔存儲檢查系統也采用布隆過濾器來檢測先前存儲的數據
- Goole Chrome瀏覽器使用了布隆過濾器加速安全瀏覽服務
- 垃圾郵件地址過濾
- 爬蟲URL地址去重
- 解決緩存穿透問題
5、Bloom Filter實戰
使用goole guava輕松實現bloom filter
源碼分析 bitArray,numHashFunction,funnel,Strategy,put(),
Demo實例
場景描述:100w字符串放入布隆過濾器,另外隨機生成1w字符串,判斷他們在100w里面是否存在
目的,了解布隆過濾器的簡單使用;
了解誤判率對hash函數個數以及bit數組長度的影響
使用bloom filter解決緩存擊穿的問題
public class BloomFilterTest { private static final int insertions = 1000000; //100w @Test public void bfTest(){ //初始化一個存儲string數據的布隆過濾器,初始化大小100w,不能設置為0 BloomFilter<String> bf = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), insertions,0.001); //初始化一個存儲string數據的set,初始化大小100w Set<String> sets = new HashSet<>(insertions); //初始化一個存儲string數據的set,初始化大小100w List<String> lists = new ArrayList<String>(insertions); //向三個容器初始化100萬個隨機並且唯一的字符串---初始化操作 for (int i = 0; i < insertions; i++) { String uuid = UUID.randomUUID().toString(); bf.put(uuid); sets.add(uuid); lists.add(uuid); } int wrong = 0;//布隆過濾器錯誤判斷的次數 int right = 0;//布隆過濾器正確判斷的次數 for (int i = 0; i < 10000; i++) { String test = i%100==0?lists.get(i/100):UUID.randomUUID().toString();//按照一定比例選擇bf中肯定存在的字符串 if(bf.mightContain(test)){ if(sets.contains(test)){ right ++; }else{ wrong ++; } } } System.out.println("=================right====================="+right);//100 System.out.println("=================wrong====================="+wrong); } }
6、解決緩存擊穿
private BloomFilter<String> bf; @postConstruct ------------->初始化的方法 private void init(){ //將唯一編碼加進來 //初始化布隆過濾器 bf = BloomFiler.create(Funnels.stringFunner(Charsets.UTF_8),編碼.size()*1.2); for(String str:ucodes){ bf.put(str); } ========將布隆過濾器的數據放到單個服務,和業務代碼分開 使用多線程放進去 if(bf.mightContain(usercode)){ return null; }
本次布隆過濾器落地場景是:優化關聯查詢
優化背景:查詢訂單需要關聯預警訂單數據,由於每查詢一筆預警就要查詢一次預警表,效率低,即是判斷該訂單是否預警
可以先將預警的訂單放到布隆過濾器中存放一份,則查詢訂單的時候可以用於關聯
應用該場景的原因:大部分訂單還是正常的,所以沒不要每次去關聯
先去布隆過濾器查詢該訂單是否存在,不存在則直接返回正常,存在則去預警表查詢,允許一定的誤差率