PHP几个几十个G大文件数据统计而且排序处理

 

诸多大互联网公司的面试都会有这么个问题,有个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.txthtm

 

出处:http://www.feiyan.info/50.htmlblog

相关文章
相关标签/搜索