本文共 2434 字,大约阅读时间需要 8 分钟。
在工作之中避免不了要对大的日志文件进行分析处理,要处理文件首先需要读取文件到内存,但是对于动辄几个G的文件,一次性读取到内存显然是不合理的,并且还需要调整配置。所以,需要借鉴MapReduce的分而治之的思想,将大文件拆分成小文件,然后再逐个分析,汇总。
php读取文件的方式对比 file_get_contents($des, null, null, $offset, $length);// 首选,从指定位置读取指定大小的数据 fopen, fread; // 含有指针,流式读取,按长度读取 fopen, fgets; // 含有指针,流式读取,按行读取 file(); // 把整个文件读入一个数组中,不适合大文件 readfile(); // 读取文件到输出缓冲,不适合
切割日志文件的方式可以按行和按大小来进行:
按大小的方式实现简单,效率相对较高,单个文件的大小确定,但是存在破损的记录; 按行的方式则可以保证记录不被损坏,效率稍微低一点。针对每个大文件,切割完了之后需要记录一份索引文件,记录小文件的存储位置名称。
有了这些小文件之后就可以采用队列的形式,使用多机器,多进程来处理他们了。
des; for($j = 0; $j < 10; $j++){ $date = ''; for($i = 0; $i < 400000; $i++){ $temp = $str . randStr() . ';' . PHP_EOL; $date .= $temp; } file_put_contents($des, $date, FILE_APPEND); } } // 切割文件,规定大小,数据存在截断的情况 public function cutData(){ $offset = 0; $i = 0; $len = $this->length * 1024 * 1024; while(true){ $data = ''; $data = file_get_contents($this->des, null, null, $offset, $len); if($data == ''){ break; } $file = 'items/bigdata_'.$i.'.log'; file_put_contents($file, $data); file_put_contents($this->index_file, $file . PHP_EOL, FILE_APPEND); $offset += $len; $i++; } } // 切割文件,按行,文件大小不精确 public function cutData2(){ $fp = fopen($this->des, 'rb');// 兼容二进制文件 $itemFileId = 0; $file = 'items/bigdata2_'.$itemFileId.'.log'; file_put_contents($this->index_file2, $file . PHP_EOL, FILE_APPEND); while(!feof($fp)){ // 判断是否已经达到文件底部 $res = $this->getLines($fp, $itemFileId); if($res == true){ $itemFileId++; $file = 'items/bigdata2_'.$itemFileId.'.log'; file_put_contents($this->index_file2, $file . PHP_EOL, FILE_APPEND); } } fclose($fp); } protected function getLines($fp, $itemFileId){ $data = ''; for($i = 0; $i < 20000; $i++){ $data .= fgets($fp); } $file = 'items/bigdata2_'.$itemFileId.'.log'; file_put_contents($file, $data, FILE_APPEND); // 注意,filesize的结果会被缓存,需清除缓存!!!,参考:https://www.php.net/manual/zh/function.clearstatcache.php // 注意: 因为 PHP 的整数类型是有符号整型而且很多平台使用 32 位整型,对 2GB 以上的文件,一些文件系统函数可能返回无法预期的结果。 $filesize = filesize($file) / (1024 * 1024); clearstatcache(); // 预估100条记录的大小,确保文件最终大小在64M左右,我这里是1M if($filesize >= ($this->length - 1)){ return true; }else{ return false; } }}$m = new BigData();$start = microtime(true);// bigdata.log 文件大小 535M$m->cutData();// 耗时:6.2122948169708 文件大小 64M$m->cutData2();// 耗时:8.6819729804993 平均文件大小 65M$end = microtime(true);echo '耗时:' . ($end - $start);
转载地址:http://vgxui.baihongyu.com/