每日一面 關於海量資料處理

2021-09-11 02:20:48 字數 3549 閱讀 4071

海量資料處理:有1億個浮點數,找出其中最大的10000個。類似的還有有1萬個浮點數,找出其中最大的100個,然後倒序處理。

二者不同之處在於資料量的量級不同,這個牽扯到伺服器記憶體和你所採取的策略。

① 簡單暴力的快速排序

第一種方法是將資料全部排序,然後在排序後的集合中進行查詢,最快的排序演算法的時間複雜度一般為o(nlogn),例如快速排序。而在32位機器上,每個float型別佔4b,1億個浮點數就要占用400m的儲存空間,對於一些可以記憶體小於400mb的計算機而言,顯然是不能一次將全部資料讀入記憶體進行排序的。其實即使記憶體能滿足要求,該方法也不高效,因為題目的目的是尋找出最大的10000個數即可,而排序是將所有元素進行排序,做了很多無用功。

② 淘汰、比較

第二種方法是淘汰法,該方法與排序方法類似,用乙個容器儲存前10000個數,然後將剩餘的所有資料一一與容器內的最小數字相比, 如果所有後續的元素都比容器內的1000個數還小,那麼容器內的這10000個數就是最大的10000個數。若某一後續袁旭比容器內的最小元素大,則刪除容器內的最小元素,並將該元素插入容器,最後遍歷完這1億個數,得到的結果容器中儲存的數即為最終結果了。此時的時間複雜度為o(n+m^2),其中m為容易的大小,即10000。

③ 分之思想

第三種方法是分治法,將1億個資料分成100份,每份100萬個資料,找出每份資料中最大的10000個,最後在剩下的100*10000個資料裡面找出最大的10000個。如果100萬資料選的足夠理想,那麼可以過濾掉1億資料裡的99%的資料。

100萬個資料裡面查詢最大的10000個資料的方法如下:用快速排序的方法,將資料分為兩堆,如果大的那堆個數n大於10000個,繼續對大堆快速排序一次分為兩堆;如果大堆個數n小於10000,就在小的那堆裡面快速排序一次,找第10000-n大的數字;遞迴以上過程,就可以找到第10000大的數。參考上述方法找出第10000大數字,就可以類似的方法找出前10000大的數字了。此種方法需要的記憶體空間為100萬*4=4m,一共需要101次這樣的比較。

④ hash法

第四種方法是hash法。如果這1億個數裡面有很多重複的數,先通過hash法,把這1億個數字去重複,這樣如果重複率很高的話,會減少很大的記憶體用量,從而縮小運算空間,然後通過分治法或者最小堆法進行查詢最大的10000個數。

⑤ 堆

第五種方法採用最小堆。先讀入前10000個數來建立大小為10000的小頂堆,建堆的時間複雜度為o(mlogm)(m為陣列的大小即為10000),然後遍歷後續的數字,並與堆頂(最小)數字進行比較。若比最小的數字小,則繼續讀取後續數字;若比堆頂數字大,則替換堆頂元素並重新調整堆作為小頂堆。

整個過程直至1億個數全都遍歷完為止.然後按照中序遍歷的方式遍歷輸出當前堆中的所有10000個數字.該演算法的時間複雜度為o(nmlogm),空間複雜度是10000(常數)。

實際上,最優的解決方案應該是最符合實際設計需求的方案,在實際應用中,可能有足夠大的記憶體,那麼直接將資料扔到記憶體中一次性處理即可,也可能機器有多個核,這樣可以採用多執行緒處理整個資料集。

ps:優先順序佇列中就維護了乙個小頂堆。

下面針對不同的應用場景,分析了適合相應應用場景的解決方案。

① 單機+單核+足夠大記憶體

例如如果需要查詢10億個查詢詞(每個佔8byte)**現頻率最高的10個,考慮到每個查詢詞佔8byte,則10億個查詢詞所需的記憶體大約是10^9*8byte = 8gb記憶體。

如果有那麼大的記憶體,直接在記憶體中對查詢詞進行排序,順序遍歷找出10個出現頻率最大的10個即可。這種方法簡單快速,更加實用。當然,也可以先用hashmap求出每個詞出現的頻率,然後求出出現頻率最大的10個詞。

② 單機+多核+足夠大記憶體

這是可以直接在記憶體中使用hash方法將資料劃分成n個partition,每個partition交給乙個執行緒處理,執行緒處理邏輯和(1)類似,最後乙個執行緒將結果合併。

該方法存在乙個瓶頸—會明顯影響效率,即資料傾斜,每個執行緒的處理速度可能不同,快的執行緒需要等待慢的執行緒,最終的處理速度取決於最慢的執行緒。而針對這個問題,解決的方法是:將資料劃分成c*n個partition(c>1),每個執行緒處理完當前partition後主動取下乙個partition繼續處理,直到所有資料處理完畢,最後由乙個執行緒將結果歸併。

可以參考forkjoin思想。

③ 單機+單核+受限記憶體

這種情況下,需要將原資料檔案切割成乙個乙個小檔案,如採用hash(x)%m,將原檔案中的資料切割成m小檔案,如果小檔案仍大於記憶體大小,繼續採用hash的方法對資料檔案進行切割,直到每個小檔案小於記憶體大小,這樣將每個檔案放到記憶體中處理,採用(1)的方法依次處理每乙個小檔案。

④ 多機+受限記憶體

這種情況下,為了合理利用多台機器的資源,可將資料分發到多台機器上,每台機器採用(3)中的策略解決本地的資料,可採用hash+socket方法進行資料分發。

從實際應用的角度考慮,(1),(2),(3),(4)方案並不可行,因為在大規模資料處理環境下,作業效率並不是要首先考慮的問題,演算法的拓展性和容錯性才是要首先考慮的。演算法應該具有良好的拓展性,以便資料量的進一步加大時,在不修改演算法框架的前提下,可達到近似的執行緒比;演算法應該具有容錯性,即當前某個檔案處理失敗後,能自動將其交給另外乙個執行緒繼續處理,而不是從頭開始處理。

top k問題很適合採用mapreduce框架解決,使用者只需編寫乙個map函式和兩個reduce函式,然後提交到hadoop(採用mapchain和reducechain)上即可解決該問題。

具體而言,就是首先根據資料值或者把資料hash(md5)後的值按照範圍劃分到不同的機器上,最好可以讓資料劃分後可以一次性讀入記憶體,這樣不同的機子負責處理各種的數值範圍,實際上就是map。得到結果後,各個機器只需拿出各自的出現次數最多的前n個資料,然後彙總,選出所有資料**現次數最多的前n個資料,這實際上就是reduce過程。對於map函式,採用hash演算法,將hash值相同的資料交給同乙個reduce task;對於第乙個reduce函式,採用hashmap統計出每個詞出現的頻率,對於第二個reduce函式,統計所喲reduce task輸出資料中的top k 即可.

直接將資料均分到不同的機器上進行處理是無法得到正確結果的。因為乙個資料可能被均分到不同的機器上,而另乙個則可能完全聚集到乙個機器上,同時還可能存在具有相同數目的資料。

例如如果要找出現次數最多的前100個,將1000萬的資料分不到10臺機器上,找到每台出現最多的前100個,歸併之後這樣不能保證找到真正的第100個。因為比如出現次數最多的前100個可能有一萬個,但是它被分到了10臺機器上,這樣每台機器上只有1000個,加上這些機器排名在1000個之前的那些都是單獨分布在一台機器上的,比如有1001個,這樣本來具有10000個的這個就會被淘汰,即使讓每台機器選出出現次數最多的1000個再歸併,仍然會出錯,因為可能存在大量的個數為1001個的傳送聚集。

因此不能將資料隨便均分到不同機器上,而是要根據hash後的值將它們對映到不同的機器上處理,讓不同的機器處理乙個數值範圍。

海量資料處理 一

想法 1.hash對映 順序讀取10個檔案,按照hash ip 10的結果將資料寫入到另外10個檔案中。2.hash統計 依次對小檔案用hash map ip,ip count 來統計每個ip出現的次數。3.堆 快速 歸併排序 利用快速 堆 歸併排序按照出現次數進行排序,將排序好的ip和對應的ip ...

海量資料處理 一

題目一 搜尋引擎會通過日誌檔案把使用者每次檢索使用的所有檢索串都記錄下來,每個查詢串的長度為1 255位元組。假設目前有一千萬個記錄 這些查詢串的重複度比較高,雖然總數是1千萬,但如果除去重複後,不超過3百萬個。乙個查詢串的重複度越高,說明查詢它的使用者越多,也就是越熱門。請你統計最熱門的10個查詢...

海量資料處理

1 有一千萬條簡訊,有重複,以文字檔案的形式儲存,一行一條,有 重複。請用5分鐘時間,找出重複出現最多的前10條。方法1 可以用雜湊表的方法對1千萬條分成若干組進行邊掃瞄邊建雜湊表。第一次掃瞄,取首位元組,尾位元組,中間隨便兩位元組作為hash code,插入到hash table中。並記錄其位址和...