php 大數據量及海量數據處理算法總結

下面的方法是我對海量數據的處理方法進行了一個通常性的總結,固然這些方法可能並不能徹底覆蓋全部的問題,可是這樣的一些方法也基本能夠處理絕大多數遇到的問題。下面的一些問題基本直接來源於公司的面試筆試題目,方法不必定最優,若是你有更好的處理方法,歡迎與我討論。 

1.Bloom filter 

適用範圍:能夠用來實現數據字典,進行數據的判重,或者集合求交集 

基本原理及要點: 
對 於原理來講很簡單,位數組+k個獨立hash函數。將hash函數對應的值的位數組置1,查找時若是發現全部hash函數對應位都是1說明存在,很明顯這 個過程並不保證查找的結果是100%正確的。同時也不支持刪除一個已經插入的關鍵字,由於該關鍵字對應的位會牽動到其餘的關鍵字。因此一個簡單的改進就是 counting Bloom filter,用一個counter數組代替位數組,就能夠支持刪除了。 

還 有一個比較重要的問題,如何根據輸入元素個數n,肯定位數組m的大小及hash函數個數。當hash函數個數k=(ln2)*(m/n)時錯誤率最小。在 錯誤率不大於E的狀況下,m至少要等於n*lg(1/E)才能表示任意n個元素的集合。但m還應該更大些,由於還要保證bit數組裏至少一半爲 0,則m 應該>=nlg(1/E)*lge 大概就是nlg(1/E)1.44倍(lg表示以2爲底的對數)。 

舉個例子咱們假設錯誤率爲0.01,則此時m應大概是n的13倍。這樣k大概是8個。 

注意這裏m與n的單位不一樣,m是bit爲單位,而n則是以元素個數爲單位(準確的說是不一樣元素的個數)。一般單個元素的長度都是有不少bit的。因此使用bloom filter內存上一般都是節省的。 

擴展: 
Bloom filter將集合中的元素映射到位數組中,用k(k爲哈希函數個數)個映射位是否全1表示元素在不在這個集合中。Counting bloom filter(CBF)將位數組中的每一位擴展爲一個counter,從而支持了元素的刪除操做。Spectral Bloom Filter(SBF)將其與集合元素的出現次數關聯。SBF採用counter中的最小值來近似表示元素的出現頻率。 

問題實例:給你A,B兩個文件,各存放50億條URL,每條URL佔用64字節,內存限制是4G,讓你找出A,B文件共同的URL。若是是三個乃至n個文件呢? 

根 據這個問題咱們來計算下內存的佔用,4G=2^32大概是40億*8大概是340億,n=50億,若是按出錯率0.01算須要的大概是650億個 bit。如今可用的是340億,相差並很少,這樣可能會使出錯率上升些。另外若是這些urlip是一一對應的,就能夠轉換成ip,則大大簡單了。 

2.Hashing 

適用範圍:快速查找,刪除的基本數據結構,一般須要總數據量能夠放入內存 

基本原理及要點: 
hash函數選擇,針對字符串,整數,排列,具體相應的hash方法。 
碰撞處理,一種是open hashing,也稱爲拉鍊法;另外一種就是closed hashing,也稱開地址法,opened addressing。 (http://www.my400800.cn) 

擴展: 
d-left hashing中的d是多個的意思,咱們先簡化這個問題,看一看2-left hashing。2-left hashing指的是將一個哈希表分紅長度相等的兩半,分別叫作T1和T2,給T1和T2分別配備一個哈希函數,h1和h2。在存儲一個新的key時,同 時用兩個哈希函數進行計算,得出兩個地址h1[key]和h2[key]。這時須要檢查T1中的h1[key]位置和T2中的h2[key]位置,哪個 位置已經存儲的(有碰撞的)key比較多,而後將新key存儲在負載少的位置。若是兩邊同樣多,好比兩個位置都爲空或者都存儲了一個key,就把新key 存儲在左邊的T1子表中,2-left也由此而來。在查找一個key時,必須進行兩次hash,同時查找兩個位置。 

問題實例: 
1).海量日誌數據,提取出某日訪問百度次數最多的那個IP。 

IP的數目仍是有限的,最多2^32個,因此能夠考慮使用hash將ip直接存入內存,而後進行統計。 

3.bit-map 

適用範圍:可進行數據的快速查找,判重,刪除,通常來講數據範圍是int的10倍如下 

基本原理及要點:使用bit數組來表示某些元素是否存在,好比8位電話號碼 

擴展:bloom filter能夠看作是對bit-map的擴展 

問題實例: 

1)已知某個文件內包含一些電話號碼,每一個號碼爲8位數字,統計不一樣號碼的個數。 

8位最多99 999 999,大概須要99m個bit,大概10幾m字節的內存便可。 

2)2.5億個整數中找出不重複的整數的個數,內存空間不足以容納這2.5億個整數。 

將bit-map擴展一下,用2bit表示一個數便可,0表示未出現,1表示出現一次,2表示出現2次及以上。或者咱們不用2bit來進行表示,咱們用兩個bit-map便可模擬實現這個2bit-map。 

4.堆 

適用範圍:海量數據前n大,而且n比較小,堆能夠放入內存 

基 本原理及要點:最大堆求前n小,最小堆求前n大。方法,好比求前n小,咱們比較當前元素與最大堆裏的最大元素,若是它小於最大元素,則應該替換那個最大元 素。這樣最後獲得的n個元素就是最小的n個。適合大數據量,求前n小,n的大小比較小的狀況,這樣能夠掃描一遍便可獲得全部的前n元素,效率很高。 

擴展:雙堆,一個最大堆與一個最小堆結合,能夠用來維護中位數。 

問題實例: 
1)100w個數中找最大的前100個數。 

用一個100個元素大小的最小堆便可。 

5.雙層桶劃分 ----其實本質上就是【分而治之】的思想,重在「分」的技巧上! 

適用範圍:第k大,中位數,不重複或重複的數字 

基本原理及要點:由於元素範圍很大,不能利用直接尋址表,因此經過屢次劃分,逐步肯定範圍,而後最後在一個能夠接受的範圍內進行。能夠經過屢次縮小,雙層只是一個例子。 

擴展: 

問題實例: 
1).2.5億個整數中找出不重複的整數的個數,內存空間不足以容納這2.5億個整數。 

有點像鴿巢原理,整數個數爲2^32,也就是,咱們能夠將這2^32個數,劃分爲2^8個區域(好比用單個文件表明一個區域),而後將數據分離到不一樣的區域,而後不一樣的區域在利用bitmap就能夠直接解決了。也就是說只要有足夠的磁盤空間,就能夠很方便的解決。 

2).5億個int找它們的中位數。 

這個例子比上面那個更明顯。首先咱們將int劃分爲2^16個區域,而後讀取數據統計落到各個區域裏的數的個數,以後咱們根據統計結果就能夠判斷中位數落到那個區域,同時知道這個區域中的第幾大數恰好是中位數。而後第二次掃描咱們只統計落在這個區域中的那些數就能夠了。 

實 際上,若是不是int是int64,咱們能夠通過3次這樣的劃分便可下降到能夠接受的程度。便可以先將int64分紅2^24個區域,而後肯定區域的第幾 大數,在將該區域分紅2^20個子區域,而後肯定是子區域的第幾大數,而後子區域裏的數的個數只有2^20,就能夠直接利用direct addr table進行統計了。 

6.數據庫索引 

適用範圍:大數據量的增刪改查 

基本原理及要點:利用數據的設計實現方法,對海量數據的增刪改查進行處理。 
擴展: 
問題實例: 


7.倒排索引(Inverted index) 

適用範圍:搜索引擎,關鍵字查詢 

基本原理及要點:爲什麼叫倒排索引?一種索引方法,被用來存儲在全文搜索下某個單詞在一個文檔或者一組文檔中的存儲位置的映射。 

以英文爲例,下面是要被索引的文本: 
T0 = "it is what it is" 
T1 = "what is it" 
T2 = "it is a banana" 
咱們就能獲得下面的反向文件索引: 
"a": {2} 
"banana": {2} 
"is": {0, 1, 2} 
"it": {0, 1, 2} 
"what": {0, 1} 
檢索的條件"what", "is" 和 "it" 將對應集合的交集。 

正 向索引開發出來用來存儲每一個文檔的單詞的列表。正向索引的查詢每每知足每一個文檔有序頻繁的全文查詢和每一個單詞在校驗文檔中的驗證這樣的查詢。在正向索引 中,文檔佔據了中心的位置,每一個文檔指向了一個它所包含的索引項的序列。也就是說文檔指向了它包含的那些單詞,而反向索引則是單詞指向了包含它的文檔,很 容易看到這個反向的關係。 

擴展: 

問題實例:文檔檢索系統,查詢那些文件包含了某單詞,好比常見的學術論文的關鍵字搜索。 

8.外排序  

適用範圍:大數據的排序,去重 

基本原理及要點:外排序的歸併方法,置換選擇 敗者樹原理,最優歸併樹 

擴展: 

問題實例: 
1).有一個1G大小的一個文件,裏面每一行是一個詞,詞的大小不超過16個字節,內存限制大小是1M。返回頻數最高的100個詞。 

這個數據具備很明顯的特色,詞的大小爲16個字節,可是內存只有1m作hash有些不夠,因此能夠用來排序。內存能夠當輸入緩衝區使用。 

9.trie樹 

適用範圍:數據量大,重複多,可是數據種類小能夠放入內存 

基本原理及要點:實現方式,節點孩子的表示方式 

擴展:壓縮實現。 

問題實例: 
1).有10個文件,每一個文件1G, 每一個文件的每一行都存放的是用戶的query,每一個文件的query均可能重複。要你按照query的頻度排序 。 

2).1000萬字符串,其中有些是相同的(重複),須要把重複的所有去掉,保留沒有重複的字符串。請問怎麼設計和實現? 

3).尋找熱門查詢:查詢串的重複度比較高,雖然總數是1千萬,但若是除去重複後,不超過3百萬個,每一個不超過255字節。 

10.分佈式處理 mapreduce 

適用範圍:數據量大,可是數據種類小能夠放入內存 

基本原理及要點:將數據交給不一樣的機器去處理,數據劃分,結果歸約。 

擴展: 

問題實例: 

1).The canonical example application of MapReduce is a process to count the appearances of 

each different word in a set of documents: 
void map(String name, String document): 
// name: document name 
// document: document contents 
for each word w in document: 
EmitIntermediate(w, 1); 

void reduce(String word, Iterator partialCounts): 
// key: a word 
// values: a list of aggregated partial counts 
int result = 0; 
for each v in partialCounts: 
result += ParseInt(v); 
Emit(result); 
Here, each document is split in words, and each word is counted initially with a "1" value by 

the Map function, using the word as the result key. The framework puts together all the pairs 

with the same key and feeds them to the same call to Reduce, thus this function just needs to 

sum all of its input values to find the total appearances of that word. 

2).海量數據分佈在100臺電腦中,想個辦法高效統計出這批數據的TOP10。 

3).一共有N個機器,每一個機器上有N個數。每一個機器最多存O(N)個數並對它們操做。如何找到N^2個數的中數(median)? 


經典問題分析 

上千萬or億數據(有重複),統計其中出現次數最多的前N個數據,分兩種狀況:可一次讀入內存,不可一次讀入。 

可用思路:trie樹+堆,數據庫索引,劃分子集分別統計,hash,分佈式計算,近似統計,外排序 

所 謂的是否能一次讀入內存,實際上應該指去除重複後的數據量。若是去重後數據能夠放入內存,咱們能夠爲數據創建字典,好比經過 map,hashmap,trie,而後直接進行統計便可。固然在更新每條數據的出現次數的時候,咱們能夠利用一個堆來維護出現次數最多的前N個數據,當 然這樣致使維護次數增長,不如徹底統計後在求前N大效率高。 

若是數據沒法放入內存。一方面咱們能夠考慮上面的字典方法可否被改進以適應這種情形,能夠作的改變就是將字典存放到硬盤上,而不是內存,這能夠參考數據庫的存儲方法。 

當 然還有更好的方法,就是能夠採用分佈式計算,基本上就是map-reduce過程,首先能夠根據數據值或者把數據hash(md5)後的值,將數據按照範 圍劃分到不一樣的機子,最好可讓數據劃分後能夠一次讀入內存,這樣不一樣的機子負責處理各類的數值範圍,實際上就是map。獲得結果後,各個機子只需拿出各 自的出現次數最多的前N個數據,而後彙總,選出全部的數據中出現次數最多的前N個數據,這實際上就是reduce過程。 

實 際上可能想直接將數據均分到不一樣的機子上進行處理,這樣是沒法獲得正確的解的。由於一個數據可能被均分到不一樣的機子上,而另外一個則可能徹底彙集到一個機子 上,同時還可能存在具備相同數目的數據。好比咱們要找出現次數最多的前100個,咱們將1000萬的數據分佈到10臺機器上,找到每臺出現次數最多的前 100個,歸併以後這樣不能保證找到真正的第100個,由於好比出現次數最多的第100個可能有1萬個,可是它被分到了10臺機子,這樣在每臺上只有1千 個,假設這些機子排名在1000個以前的那些都是單獨分佈在一臺機子上的,好比有1001個,這樣原本具備1萬個的這個就會被淘汰,即便咱們讓每臺機子選 出出現次數最多的1000個再歸併,仍然會出錯,由於可能存在大量個數爲1001個的發生彙集。所以不能將數據隨便均分到不一樣機子上,而是要根據hash 後的值將它們映射到不一樣的機子上處理,讓不一樣的機器處理一個數值範圍。 

而外排序的方法會消耗大量的IO,效率不會很高。而上面的分佈式方法,也能夠用於單機版本,也就是將總的數據根據值的範圍,劃分紅多個不一樣的子文件,而後逐個處理。處理完畢以後再對這些單詞的及其出現頻率進行一個歸併。實際上就能夠利用一個外排序的歸併過程。 

另外還能夠考慮近似計算,也就是咱們能夠經過結合天然語言屬性,只將那些真正實際中出現最多的那些詞做爲一個字典,使得這個規模能夠放入內存。
相關文章
相關標籤/搜索