php处理大文件文件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了php处理大文件文件相关的知识,希望对你有一定的参考价值。

web 服务器每天会产生大量的访问日志,日志为文本文件,每一次访问为一次记录,在日志中产生一行记录,每天的访问 pv 在 1 亿左右,即每天产生 1 亿条日志记录,日志大小在28G 左右,现需要将日志文件中有用的数据进行计算处理之后放入到 mysql 数据库中。

日志格式如下:
服务器响应状态/alipeng.gif?请求的参数
日志的样本如下(更多数据见附件 access.log):
200/alipeng.gif?zoneid=2&bannerid=44&clentid=6&materialid=64&redirect=http
%3a%2f
%2fwww.alipeng.cn&time=1384503191.832&ip=127.0.0.1&user_agent=Mozilla/5.0(X11;
Linuxx86_64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/31.0.1650.48
Safari/537.36&utrace=a6dbdd2f6a37b946165b7ae98dcd4f79
要求:
1).需要将状态为 200(即访问成功)的记录分析计算处理放入 MySQL 中;
2).需要入库的数据有 zoneid 、bannerid 、clentid 、materialid、redirect 、time、user_agent、utrace 、sum,其中 time 保存为时间格式,如2013-11-27 13,只到小时,sum 是当zoneid、 bannerid、 clentid、materialid、redirect、user_agent、utrace 相同并且time 在同一个小时内时的访问次数,即同一小时内相同记录自增 1;
3). 如果有余力时可将 user_agent 拆分为浏览器类型,操作系统类型;
4).要求 1 亿条日志记录能在 20 小时内处理完毕;
5).要求写出数据库建表语句,php 代码并作好注释。
关键点:
1 亿条日志记录 数据库设计 代码效率 20 小时内处理完毕

参考技术A

我前几天有一个面试,面试题就是有这样一道题。先把自己的思路说一下,因为信息量非常的大,所以我采用了分表,分成24张表,每个小时一张,虽然凌晨时刻的表可能很少数据,但这样sum字段的问题就容易解决了,我理解的sum字段是一个小时同一个用户在相同的环境的登陆次数。这样理解不知对否,请网友自行甄辨。然后我通过PHP中的fgets函数一行一行的数据取出,入表。实验了几万条数据是没有问题的,但是上亿条数据可能够呛。这一点也请网友注意,我也是新手。只是看到这里没有答案,给大家一个参考。废话不多,看流程:

日志文件(access.log)格式:

200 /alipeng.gif?zoneid=2&bannerid=44&clentid=6&materialid=64&redirect=http%3a%2f%2fwww.alipeng.cn&time=1384444800.832&ip=127.0.0.1&user_agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36&utrace=a6dbdd2f6a37b946165b7ae98dcd4f79
502 /alipeng.gif?zoneid=2&bannerid=44&clentid=6&materialid=64&redirect=http%3a%2f%2fwww.alipeng.org&time=1384444800.904&ip=127.0.0.1&user_agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36&utrace=a6dbdd2f6a37b946165b7ae98dcd4f79


配置文件cfg.php:

    define(HOST,'localhost');//主机名
    define(USER,'root');//数据库账号
    define(PASS,'111111');//数据库密码
    define(DBNAME,'test');//所用的数据库
    define(CHARSET,'utf8');//使用的字符集

具体代码test.php:

<?php
header("content-type:text/html;charset=utf-8");
require './cfg.php';

    $link = mysql_connect(HOST,USER,PASS) or die('连接数据库失败');

//程序中自动建库和建表,这样一定程度上拖慢了程序的速度
//创建数据库
$crdb="create database if not exists ".DBNAME;
if(!mysql_query($crdb))
die('创建数据库失败');


//链接数据库
    mysql_select_db(DBNAME) or die('选择数据库失败');
    mysql_set_charset(CHARSET);

//因为数据量很大我将数据按小时分表,分成24个表,每小时一个表,这样num字段的值也好做统计
//数据循环建表
for($i=0;$i<24;$i++)
if($i<10)
$tbhz='0'.$i;//如果前10张表,表后缀应该是00-09
else
$tbhz=$i;

$ctbsql="create table if not exists logininfo_$tbhz(
id int not null auto_increment primary key,
zoneid int not null default 0,
bannerid int not null default 0,
clentid int not null default 0,
materialid int not null default 0,
redirect char(200) not null default '',
time char(16) not null default '',
user_agent char(200) not null default '',
utrace char(32) not null default '',
sum int not null default 0
)TYPE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci";
mysql_query($ctbsql);




//打开文件
$file=fopen("./access.log",'r') or die("打开文件失败");
//对文件内容进行循环,直到文件末尾才停止
while (!feof($file))
//每次读取一行
$line = fgets($file,1024);
//状态是200的进行写入数据库操作
if(preg_match('/^200/',$line))
$pinfo=parse_url($line);//url信息
$ext=$pinfo['query'];//取得传递的各个参数
$parray=explode('&',$ext);//根据&分解为数组

//因为分解为数组后并不是要的值,所以要对值进行一次截取,将等号及等号左边的都去掉
foreach($parray as $val)
$narray[]=ltrim(strstr($val,'='),'=');

$narray[8]=rtrim($narray[8],'_');
//截取时间的秒数
$getmun=substr($parray[5],5,10);
$time=date('Y-m-d H',$getmun);//将秒数转化为时间类型。

//得到表后缀
$tbhz=date('H',$getmun);

$sql="insert into logininfo_$tbhz values(null,'$narray[0]','$narray[1]','$narray[2]','$narray[3]','$narray[4]','$time','$narray[7]','$narray[8]',0)";

//echo $sql;
$res=mysql_query($sql);//执行插入
if(!$res || !mysql_affected_rows()>0)
die('写入数据库失败');


unset($narray);//循环一次将narray销毁,为下一次循环做准备
//var_dump(parse_url($line)['query']);



fclose($file);//关闭

//因为sum字段还是0,下面代码段需要处理sum字段的值

//24张表循环处理
for($i=0;$i<24;$i++)
if($i<10)
$tbhz='0'.$i;//如果前10张表,表后缀应该是00-09
else
$tbhz=$i;


//该sql语句是把同一个小时内,并且符合条件相等的登陆的总次数和需要的登陆信息查出,为下面修改sum做准备
$sql="SELECT COUNT('zoneid') AS sum,zoneid,bannerid,clentid,materialid,redirect,user_agent,utrace FROM logininfo_$tbhz GROUP BY zoneid,bannerid,clentid,materialid,redirect,user_agent,utrace";
//发送查询sql
$res=mysql_query($sql);

if($res && mysql_num_rows($res)>0)
while($row=mysql_fetch_assoc($res))
//修改sum字段,即同一小时内的登陆次数
$upsql="update logininfo_$tbhz set sum='$row['sum']' where zoneid='$row['zoneid']' and bannerid='$row['bannerid']' and clentid='$row['clentid']' and materialid='$row['materialid']' and redirect='$row['redirect']' and user_agent='$row['user_agent']' and utrace='$row['utrace']'";
//发送修改sql,执行修改sum
$upres=mysql_query($upsql);
if(!$upres)
die('修改登陆sum失败');






echo '数据成功入表';

使用说明:

    将配置文件cfg.php中的连接数据库账号、密码修改为自己本机的(默认新增的库名是test)

    直接运行test1.php

参考技术B pv 一亿的网站,好厉害的样子。

以上是关于php处理大文件文件的主要内容,如果未能解决你的问题,请参考以下文章

PHP处理大文件下载

PHP处理大文件下载

Php处理大文件-分割和合并

PHP有限内存处理大文件(从两个文件提取相同行)

PHP有限内存处理大文件(从两个文件提取相同行)

php使用yield处理大数据文件