php处理大的日志文件
2012-11-08 10:49
267 查看
php为了防止内存占用过多,在php.ini限制了内存占用,memory_limit = 16M默认为16M,可
以修改脚本占用内存限制,比如为-1,无限制。
做了如下实验。
场景:
一个1G左右的日志文件,大约700万行。
方案:
一、file函数直读。
代码如下
ini_set('memory_limit','-1');
$file = 'access.log';
$data = file($file);
$line = $data[count($data)-1];
echo $line;
机器直接无响应。。大概运行了200多秒。。
二、调用linux的tail函数
在linux命令行下,可以直接使用tail -n 10 access.log很轻易的显示日志文件某行。
file = 'access.log';
$file = escapeshellarg($file); // 对命令行参数进行安全转义
$line = `tail -n 1 $file`;
echo $line;
时间:0.0045秒,已经达到可用标准,但对环境有要求。
三、fseek函数
fseek函数不需要把文件全部装入内存,而是直接通过指针来操作,所以效率很高。代码:
$fp = fopen($file, "r");
$line = 10;
$pos = -2;
$t = " ";
$data = "";
while ($line > 0) {
while ($t != "\n") {
fseek($fp, $pos, SEEK_END);
$t = fgetc($fp);
$pos --;
}
$t = " ";
$data .= fgets($fp);
$line --;
}
fclose ($fp);
echo $data
时间:0.0105秒
再提升一点,按块读取,然后计算块内的\n标记来算到了多少行。
$fp = fopen($file, "r");
$num = 10;
$chunk = 4096;
$fs = sprintf("%u", filesize($file));
$max = (intval($fs) == PHP_INT_MAX) ? PHP_INT_MAX : filesize($file);
for ($len = 0; $len < $max; $len += $chunk) {
$seekSize = ($max - $len > $chunk) ? $chunk : $max - $len;
fseek($fp, ($len + $seekSize) * -1, SEEK_END);
$readData = fread($fp, $seekSize) . $readData;
if (substr_count($readData, "\n") >= $num + 1) {
preg_match("!(.*?\n){".($num)."}$!", $readData, $match);
$data = $match[0];
break;
}
}
fclose($fp);
echo $data;
时间:0.002秒
再快一点的话,可以判断如果行数大于一半,可以从后往前取,这样二分之后,取后面的速度会
更加快速。
function tail($fp,$n,$base=5)
{
assert($n>0);
$pos = $n+1;
$lines = array();
while(count($lines)< =$n){
try{
fseek($fp,-$pos,SEEK_END);
} catch (Exception $e){
fseek(0);
break;
}
$pos *= $base;
while(!feof($fp)){
array_unshift($lines,fgets($fp));
}
}
return array_slice($lines,0,$n);
}
var_dump(tail(fopen("access.log","r+"),10));
实际应用中再根据具体情况调整。
以修改脚本占用内存限制,比如为-1,无限制。
做了如下实验。
场景:
一个1G左右的日志文件,大约700万行。
方案:
一、file函数直读。
代码如下
ini_set('memory_limit','-1');
$file = 'access.log';
$data = file($file);
$line = $data[count($data)-1];
echo $line;
机器直接无响应。。大概运行了200多秒。。
二、调用linux的tail函数
在linux命令行下,可以直接使用tail -n 10 access.log很轻易的显示日志文件某行。
file = 'access.log';
$file = escapeshellarg($file); // 对命令行参数进行安全转义
$line = `tail -n 1 $file`;
echo $line;
时间:0.0045秒,已经达到可用标准,但对环境有要求。
三、fseek函数
fseek函数不需要把文件全部装入内存,而是直接通过指针来操作,所以效率很高。代码:
$fp = fopen($file, "r");
$line = 10;
$pos = -2;
$t = " ";
$data = "";
while ($line > 0) {
while ($t != "\n") {
fseek($fp, $pos, SEEK_END);
$t = fgetc($fp);
$pos --;
}
$t = " ";
$data .= fgets($fp);
$line --;
}
fclose ($fp);
echo $data
时间:0.0105秒
再提升一点,按块读取,然后计算块内的\n标记来算到了多少行。
$fp = fopen($file, "r");
$num = 10;
$chunk = 4096;
$fs = sprintf("%u", filesize($file));
$max = (intval($fs) == PHP_INT_MAX) ? PHP_INT_MAX : filesize($file);
for ($len = 0; $len < $max; $len += $chunk) {
$seekSize = ($max - $len > $chunk) ? $chunk : $max - $len;
fseek($fp, ($len + $seekSize) * -1, SEEK_END);
$readData = fread($fp, $seekSize) . $readData;
if (substr_count($readData, "\n") >= $num + 1) {
preg_match("!(.*?\n){".($num)."}$!", $readData, $match);
$data = $match[0];
break;
}
}
fclose($fp);
echo $data;
时间:0.002秒
再快一点的话,可以判断如果行数大于一半,可以从后往前取,这样二分之后,取后面的速度会
更加快速。
function tail($fp,$n,$base=5)
{
assert($n>0);
$pos = $n+1;
$lines = array();
while(count($lines)< =$n){
try{
fseek($fp,-$pos,SEEK_END);
} catch (Exception $e){
fseek(0);
break;
}
$pos *= $base;
while(!feof($fp)){
array_unshift($lines,fgets($fp));
}
}
return array_slice($lines,0,$n);
}
var_dump(tail(fopen("access.log","r+"),10));
实际应用中再根据具体情况调整。
相关文章推荐
- PowerMTA执行php文件处理发送邮件的日志信息
- 解读PHP中上传文件的处理问题
- SQL数据库文件和数据库日志文件均非常大,处理办法
- php+mysql实现大文件处理
- PHP文件处理与文件上传
- php文件之间相互引用路径问题的一般处理方法
- php文件下载处理方法分析
- PHP源码安装后找不到配置文件的问题处理
- PHP 简单处理--文件下载--文件上传
- 上传图片文件用PHP处理
- php使用指定的文件记录错误报告日志
- PHP保存本地日志文件
- PHP学习文件处理与文件上传 课件第1/2页
- PHP学习文件处理与文件上传 课件第1/2页
- php单例模式实现日志处理类库
- Spring-Boot--日志操作【全局异常捕获消息处理☞日志控制台输出+日志文件记录】
- 数据库日志文件过大的处理方法
- AIX 日志清理 监听日志清理--过大的Oracle监听日志文件处理
- Oracle 日志文件错误的处理。
- php生成日志文件(往内接着添加内容)