面試

1. 問題描述

在大規模資料處理中,常遇到的一類問題是,在海量資料中找出出現頻率最高的前K個數,或者從海量資料中找出最大的前K個數,這類問題通常稱為“top K”問題,如:在搜尋引擎中,統計搜尋最熱門的10個查詢詞;在歌曲庫中統計下載率最高的前10首歌等等。

2. 當前解決方案

針對top k類問題,通常比較好的方案是【分治+trie樹/hash+小頂堆】,即先將資料集按照hash方法分解成多個小資料集,然後使用trie樹或者hash統計每個小資料集中的query詞頻,之後用小頂堆求出每個資料集中出頻率最高的前K個數,最後在所有top K中求出最終的top K。

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

本文針對不同的應用場景,介紹了適合相應應用場景的解決方案。

3. 解決方案

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

設每個查詢詞平均佔8Byte,則10億個查詢詞所需的記憶體大約是10^9*8=8G記憶體。如果你有這麼大的記憶體,直接在記憶體中對查詢詞進行排序,順序遍歷找出10個出現頻率最大的10個即可。這種方法簡單快速,更加實用。當然,也可以先用HashMap求出每個詞出現的頻率,然後求出出現頻率最大的10個詞。

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

這時可以直接在記憶體中實用hash方法將資料劃分成n個partition,每個partition交給一個執行緒處理,執行緒的處理邏輯是同3.1節類似,最後一個執行緒將結果歸併。

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

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

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

3.4 多機+受限記憶體

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

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

Top k問題很適合採用MapReduce框架解決,使用者只需編寫一個map函式和兩個reduce 函式,然後提交到Hadoop(採用mapchain和reducechain)上即可解決該問題。對於map函式,採用hash演算法,將hash值相同的資料交給同一個reduce task;對於第一個reduce函式,採用HashMap統計出每個詞出現的頻率,對於第二個reduce 函式,統計所有reduce task輸出資料中的top k即可。

4. 總結

Top K問題是一個非常常見的問題,公司一般不會自己寫個程式進行計算,而是提交到自己核心的資料處理平臺上計算,該平臺的計算效率可能不如直接寫程式高,但它具有良好的擴充套件性和容錯性,而這才是企業最看重的。

自:36大資料