諸多大互聯網公司的面試都會有這麼個問題,有個4G的文件,如何用只有1G內存的機器去計算文件中出現次數最多的數字(假設1行是1個數組,例如QQ號 碼)。若是這個文件只有4B或者幾十兆,那麼最簡單的辦法就是直接讀取這個文件後進行分析統計。可是這個是4G的文件,固然也多是幾十G甚至幾百G的文 件,這就不是直接讀取能解決了的。php
一樣對於如此大的文件,單純用PHP作是確定行不通的,個人思路是無論多大文件,首先要切割爲多個應用能夠承受的小文件,而後批量或者依次分析統計小文件後再把總的結果彙總後統計出符合要求的最終結果。相似於比較流行的MapReduce模型,其核心思想就是「Map(映射)」和「Reduce(化簡)」,加上分佈式的文件處理,固然我能理解和使用到的只有Reduce後去處理。html
假設有1個10億行的文件,每行一個6位-10位不等的QQ號碼,那麼我須要解決的就是計算在這10億個QQ號碼中,重複最多的前10個號碼,使用下面的PHP腳本生成這個文件,極可能這個隨機數中不會出現重複,可是咱們假設這裏面會有重複的數字出現。面試
$fp = fopen('qq.txt','w+'); for( $i=0; $i<1000000000; $i++ ){ $str = mt_rand(10000,9999999999)."\n"; fwrite($fp,$str); } fclose($fp);
生成文件的世界比較長,Linux下直接使用php-client運行PHP文件會比較節省時間,固然也可使用其餘方式生成文件。生成的文件大約11G。算法
而後使用Linux Split切割文件,切割標準爲每100萬行數據1個文件。數組
split -l 1000000 -a 3 qq.txt qqfile
qq.txt被分割爲名字是qqfileaaa到qqfilebml的1000個文件,每一個文件11mb大小,這時再使用任何處理方法都會比較簡單了。我仍是使用PHP進行分析統計:分佈式
$results = array(); foreach( glob('/tmp/qq/*') as $file ){ $fp = fopen($file,'r'); $arr = array(); while( $qq = fgets($fp) ){ $qq = trim($qq); isset($arr[$qq]) ? $arr[$qq]++ : $arr[$qq]=1; } arsort($arr); //如下處理方式存在問題 do{ $i=0; foreach( $arr as $qq=>$times ){ if( $i > 10 ){ isset($results[$qq]) ? $results[$qq]+=$times : $results[$qq]=$times; $i++; } else { break; } } } while(false); fclose($fp); } if( $results ){ arsort($results); do{ $i=0; foreach( $results as $qq=>$times ){ if( $i > 10 ){ echo $qq . "\t" . $times . "\n"; $i++; } else { break; } } } while(false); }
這樣每一個樣本取前10個,最後放到一塊兒分析統計,不排除有個數在每一個樣本中都排名第11位可是總數絕對在前10的可能性,因此後面統計計算算法還須要改進。spa
也許有人說使用Linux中的awk和sort命令能夠完成排序,可是我試了下若是是小文件還能夠實現,可是11G的文件,無論是內存仍是時間都沒法承受。下面是我改的1個awk+sort的腳本,或許是寫法有問題,求牛人指導。code
awk -F '\\@' '{name[$1]++ } END {for (count in name) print name[count],count}' qq.txt |sort -n > 123.txt
awk
-F
'\\@'
'{name[$1]++ } END {for (count in name) print name[count],count}'
qq.txt |
sort
-n > 123.txt
htm
出處:http://www.feiyan.info/50.htmlblog