php切割大的日志文件的方法

在工作之中避免不了要对大的日志文件进行分析处理,要处理文件首先需要读取文件到内存,但是对于动辄几个G的文件,一次性读取到内存显然是不合理的,并且还需要调整配置。所以,需要借鉴MapReduce的分而治之的思想,将大文件拆分成小文件,然后再逐个分析,汇总。

php读取文件的方式对比
	file_get_contents($des, null, null, $offset, $length);// 首选,从指定位置读取指定大小的数据
	fopen, fread; // 含有指针,流式读取,按长度读取
	fopen, fgets; // 含有指针,流式读取,按行读取
	file(); // 把整个文件读入一个数组中,不适合大文件
	readfile(); // 读取文件到输出缓冲,不适合

切割日志文件的方式可以按行和按大小来进行:
按大小的方式实现简单,效率相对较高,单个文件的大小确定,但是存在破损的记录;
按行的方式则可以保证记录不被损坏,效率稍微低一点。

针对每个大文件,切割完了之后需要记录一份索引文件,记录小文件的存储位置名称。

有了这些小文件之后就可以采用队列的形式,使用多机器,多进程来处理他们了。

<?php

class BigData{

	public $des = 'bigdata.log';
	public $index_file = 'bigdata_index.log';
	public $index_file2 = 'bigdata_index2.log';
	public $length = 64; // 64M

	public function randStr(){
		$base = join('', array_merge(range(0, 9) , range('a', 'z') , range('A', 'Z')));
		$len = rand(20, 50);
		$out = '';
		for($i = 0; $i < $len; $i++){
			$out .= $base[rand(0, 42)];
		}

		return $out;
	}

	// 生产大文件
	public function create(){
		$str = '[ 2019-10-09T16:34:36+08:00 ] 127.0.0.1 127.0.0.1 POST /site/product/getdetailtableindustry.html?a=';

		$des = $this->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);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值