問題
假設你現在要處理這樣乙個問題,你有乙個**並且擁有很多訪客,每當有使用者訪問時,你想知道這個ip是不是第一次訪問你的**。這是乙個很常見的場景,為了完成這個功能,你很容易就會想到下面這個解決方案:
把訪客的ip存進乙個hash表中,每當有新的訪客到來時,先檢查雜湊表中是否有改訪客的ip,如果有則說明該訪客在黑名單中。你還知道,hash表的訪問時間複雜度都是o(1),效率很高,因此你對你的方案很是滿意。
然後我們假設你的**已經被1億個使用者訪問過,每個ip的長度是15,那麼你一共需要15 * 100000000 = 1500000000bytes = 1.4g,這還沒考慮hash衝突的問題(hash表中的槽位越多,越浪費空間,槽位越少,效率越低)。
於是聰明的你稍一思考,又想到可以把ip轉換成無符號的int型值來儲存,這樣乙個ip只需要占用4個位元組就行了,這時1億個ip占用的空間是4 * 100000000 = 400000000bytes = 380m,空間消耗降低了很多。
那還有沒有在不影響訪問效率的前提下更加節省空間的辦法呢?
有bitset
32位無符號int型能表示的最大值是4294967295,所有的ip都在這個範圍內,我們可以用乙個bit位來表示某個ip是否出現過,如果出現過,就把代表該ip的bit位置為1,那麼我們最多需要429496729個bit就可以表示所有的ip了。舉個例子比如10.0.0.1轉換成int是167772161,那麼把長度為4294967295的bit陣列的第167772161個位置置為1即可,當有ip訪問時,只需要檢查該標誌位是否為1就行了。
4294967295bit = 536870912byte = 512m。如果用hash表示所有4294967295範圍內的陣列的話,需要十幾g的空間。
當然,這裡舉ip的例子不一定合適,主要目的是為了引出bitset。
下面我們來看看bitset具體怎樣實現。
首先,比如我們有乙個長度=2的byte陣列,2個位元組一共有16位,可以表示0-15的數字是否存在。比如我們要驗證11是否出現過,那麼我們先檢查第11個位置是否為1,如果為0,說明11沒出現過,然後我們把第11位置為1,表示11已經出現過了
所以,bitset基本只有兩個操作,set(int value) 和 ishas(int value)
bitset的侷限性
想必有同學已經意識到了bitset使用起來似乎並不總是那麼完美,bitset有兩個比較侷限的地方:
當樣本分佈極度不均勻的時候,bitset會造成很大空間上的浪費。
舉個例子,比如你有10個數,分別是1、2、3、4、5、6、7、8、99999999999;那麼你不得不用99999999999個bit位去實現你的bitset,而這個bitset的中間絕大多數字置都是0,並且永遠不會用到,這顯然是極度不划算的。
當元素不是整型的時候,bitset就不適用了。
想想看,你拿到的是一堆url,然後如果你想用bitset做去重的話,先得把url轉換成int型,在轉換的過程中難免某些url會計算出相同的int值,於是bitset的準確性就會降低。
那針對這兩種情況有沒有解決辦法呢?
第一種分布不均勻的情況可以通過hash函式,將元素都對映到乙個區間範圍內,減少大段區間閒置造成的浪費,這很簡單,取模就好了,難的是取模之後的值保證不相同,即不發生hash衝突。
第二種情況,把字串對映成整數是必要的,那麼唯一要做的就是保證我們的hash函式盡可能的減少hash衝突,一次不行我就多hash幾次,hash還是容易碰撞,那我就擴大陣列的範圍,使hash值盡可能的均勻分布,減少hash衝突的概率。
基於這種思想,bloomfilter誕生了。
bloomfilter
實現原理
bloomfiler又叫布隆過濾器,下面舉例說明bloofilter的實現原理:
比如你有10個url,你完全可以建立一長度是100bit的陣列,然後對url分別用5個不同的hash函式進行hash,得到5個hash後的值,這5個值盡可能的保證均勻分布在100個bit的範圍內。然後把5個hash值對應的bit位都置為1,判斷乙個url是否已經存在時,一次看5個bit位是否為1就可以了,如果有任何乙個不為1,那麼說明這個url不存在。這裡需要注意的是,如果對應的bit位值都為1,那麼也不能肯定這個url一定存在,這個是bloomfilter的特點之一,稍後再說。
核心思想
bloomfilter的核心思想有兩點:
多個hash,增大隨機性,減少hash碰撞的概率
擴大陣列範圍,使hash值均勻分布,進一步減少hash碰撞的概率。
bloomfilter的準確性
儘管bloomfilter已經盡可能的減小hash碰撞的概率了,但是,並不能徹底消除,因此正如上面提到的:
如果對應的bit位值都為1,那麼也不能肯定這個url一定存在
也就是說,bloomfilter其實是存在一定的誤判的,這個誤判的概率顯然和陣列的大小以及hash函式的個數以及每個hash函式本身的好壞有關,具體的計算公式,可以查閱相關**,這裡只給出結果:
wiki的bloom filter詞條有關於誤報的概率的詳細分析:probability of false positives。從分析可以看出,誤判概率還是比較小的,空間利用率也很高。
bloomfilter的應用
黑名單比如郵件黑名單過濾器,判斷郵件位址是否在黑名單中
排序(僅限於bitset)
仔細想想,其實bitset在set(int value)的時候,「順便」把value也給排序了。
網路爬蟲
判斷某個url是否已經被爬取過
k-v系統快速判斷某個key是否存在
典型的例子有hbase,hbase的每個region中都包含乙個bloomfilter,用於在查詢時快速判斷某個key在該region中是否存在,如果不存在,直接返回,節省掉後續的查詢。
大資料面試遇到的問題
1.yarn排程有哪幾種方式,優缺點是什麼?job 占用相同資源 2.大資料集群資料丟失了如何恢復?3.你平時遇到過那些故障並且如何解決?4.namenode如何優化?5.namenode啟動流程是什麼?6.flume 有幾種模式,你們常用那些模式?7.hadoop如何調優?8.kafka執行流程圖...
大資料面試
資料分析師常見的10道面試題解答 資料分析師 或者如下闡述 演算法思想 分而治之 hash ip位址最多有2 32 4g種取值情況,所以不能完全載入到記憶體中處理 可以考慮採用 分而治之 的思想,按照ip位址的hash ip 24值,把海量ip日誌分別儲存到1024個小檔案中。這樣,每個小檔案最多包...
大資料面試總結
1 給出乙個超過100g的log file,log中存著ip位址,設計演算法找到出現次數最多的ip位址?採用雜湊切割將ip相同的檔案都對映到同乙個檔案中,在一次統計每個檔案ip的個數,求出最多的,如果乙個ip出現的次數特別多,切割之後還是無法載入到記憶體中,我們可在對這個檔案進行切割 普通切割 分成...