在100G文件中找出出現次數最多的100個IP(轉)


昨天面阿里最后栽在一道很常見的海量數據處理上了,也怪之前沒專門花時間准備這個問題。今天參考了July的博客,又反思了下自己面試時錯誤的思路,重新整理為下面的解答過程。

先上July的博客對類似問題的解答思路:

搜索引擎會通過日志文件把用戶每次檢索使用的所有檢索串都記錄下來,每個查詢串的長度為1-255字節。假設目前有一千萬個記錄(這些查詢串的重復度比較高,雖然總數是1千萬,但如果除去重復后,不超過3百萬個。一個查詢串的重復度越高,說明查詢它的用戶越多,也就是越熱門),請你統計最熱門的10個查詢串,要求使用的內存不能超過1G。

    解答:我們知道,數據大則划為小的,如如一億個Ip求Top 10,可先%1000將ip分到1000個小文件中去,並保證一種ip只出現在一個文件中,再對每個小文件中的ip進行hashmap計數統計並按數量排序,最后歸並或者最小堆依次處理每個小文件的top10以得到最后的結。

    但如果數據規模比較小,能一次性裝入內存呢?比如這第2題,雖然有一千萬個Query,但是由於重復度比較高,因此事實上只有300萬的Query,每個Query255Byte,因此我們可以考慮把他們都放進內存中去(300萬個字符串假設沒有重復,都是最大長度,那么最多占用內存3M*1K/4=0.75G。所以可以將所有字符串都存放在內存中進行處理),而現在只是需要一個合適的數據結構,在這里,HashTable絕對是我們優先的選擇。

    所以我們放棄分而治之/hash映射的步驟,直接上hash統計,然后排序。So,針對此類典型的TOP K問題,采取的對策往往是:hashmap + 堆。如下所示:

hash_map統計:先對這批海量數據預處理。具體方法是:維護一個Key為Query字串,Value為該Query出現次數的HashTable,即hash_map(Query,Value),每次讀取一個Query,如果該字串不在Table中,那么加入該字串,並且將Value值設為1;如果該字串在Table中,那么將該字串的計數加一即可。最終我們在O(N)的時間復雜度內用Hash表完成了統計;
堆排序:第二步、借助堆這個數據結構,找出Top K,時間復雜度為N‘logK。即借助堆結構,我們可以在log量級的時間內查找和調整/移動。因此,維護一個K(該題目中是10)大小的小根堆,然后遍歷300萬的Query,分別和根元素進行對比。所以,我們最終的時間復雜度是:O(N) + N' * O(logK),(N為1000萬,N’為300萬)。
接下來再來說說我遇到的這個題,幾乎就按這個思路來就好了:
對於100G的文件,先算算能有多少條IP呢?每條IP最長為15個字節,則100G/15=6.7G條,IP一共有多少種呢,不考慮IPv6,約有256^4=2^32條=4G條,那么最極端的情況是每種IP都有,每個都出現那么一兩條。

 

要解決該問題首先要找到一種分類方式,把重復出現的IP都放到一個文件里面,一共分成100份,這可以通過把IP對100取模得到,具體方法如去掉IP中的點轉化為一個long型變量,這樣取模為0,1,2...99的IP都分到一個文件了,那么這個分就能保證每一文件都能載入內存嗎?這可不一定,萬一模為9的IP特別多怎么辦,可以再對這一類IP做一次取模,直到每個小文件足夠載入內存為止。這個分類很關鍵,如果是隨便分成100份,相同的IP被分在了不同的文件中,接下來再對每個文件統計次數並做歸並,這個思路就沒有意義了,起不到“大而化小,各個擊破,縮小規模,逐個解決”的效果了。

 

好了,接下來把每個小文件載入內存,建立哈希表unordered_map<string,int>,將每個IP作為關鍵字映射為出現次數,這個哈希表建好之后也得先寫入硬盤,因為內存就那么多,一共要統計100個文件呢。

 

在統計完100個文件之后,我再建立一個小頂堆,大小為100,把建立好並存在硬盤哈希表載入內存,逐個對出現次數排序,挑出出現次數最多的100個,由於次數直接和IP是對應的,找出最多的次數也就找出了相應的IP。

 

這只是個大致的算法,還有些細節,比如第90到110大的元素出現次數一樣呢,就隨機舍棄掉10個嗎?整個的時間復雜度分類O(n),建哈希表O(n),挑出出現最多次數的O(nlogk)
————————————————
版權聲明:本文為CSDN博主「不舍駑馬」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/fycy2010/article/details/46945641

我遇到的問題:

1、  日志文本,都是電話號碼,統計出現次數前一百的電話號碼。如果文本很大,5000億,其中有10億不重復的電話號碼,8G內存,硬盤很大,應該怎么做。

答:(1)對所有電話號碼進行轉換,將其由字符串轉換為long類型變量,每4位表示一個號碼數字。總文本大小約為4000G。

(2)根據首三位號碼數字進行切分,將其分割為1000個小文件,如果某個文件過大,則再按第四位號碼數字將其繼續切分,直到小文件足夠載入內存。且保證了相同電話號碼在同一小文件中。

(3)依次讀取小文件,並建立unordered_map<long,long>,key為電話號碼,value為次數。將統計好的哈希表也寫入文件。

(4)建立小根堆,大小為100,將建立好的哈希表文件依次讀取,並比對出現次數,維護堆,同時維護處於堆中的對應電話號碼,最后得到出現次數前一百的電話號碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM