2013-10-26 09:34:01
来 源
kejihao
Apache
这里分享一下Apache服务器日志分析,希望对于初学Apache服务器相关的朋友有帮助,更多Apache安装、配置、报错处理等资源请本站内搜索。

问题背景:

一个新闻网站,其新闻的URL如下形式:

http://www.example.com/archive/yyyy/mm/dd/id.shtml

想要每天早上能得到前一天网站新闻的点击情况(PV和IP),以方便根据浏览者的喜好对新闻内容做出调整,新闻都已经是静态化成“.shtml”格式的文件。最一般的情况,可以在每个文章的静态文件里加入类似下面的代码来完成计数统计:

<script scr="example.php?id=123"></script>

然后在相应的PHP文件里完成PV和IP的入库工作。

不过因为网站访问量巨大,数据库无法承受每次浏览时更新PV和IP数据的压力,所以需要换一种流量统计的方法。

解决思路:

首先,调整Apache日志的记录格式:

LogFormat ""%h","%U"" NewsLog

这里,我为了简单,只记录了主机地址(%h)和文件路径(%U),实际为了更准确实用,你往往还需要加入别的设置。具体参数格式可以参考如下链接:

http://httpd.apache.org/docs/2.0/mod/mod_log_config.html#formats

然后,我们还需要告诉Apache应该记录哪些访问的日志,免得记录了我们不想要的东西。

根据我们的URL样式:

http://www.example.com/archive/yyyy/mm/dd/id.shtml

SetEnvIf Request_URI "^/archive/" NewsEnv

具体的匹配模式不用写的分毫不差,只要根据客观情况能让Apache做出正确的判断就可以了,这样也可以提高效率。经过这样的配置后后日志就只会记录

http://www.example.com/archive/yyyy/mm/dd/id.shtml

的日志,而不会记录类似

http://www.example.com/index.shtml

的日志,这样保证了结果的客观性。

然后我们还需要在相应的VirtualHost里配置CustomLog选项,加入我们上面设定的规则,具体如下:

CustomLog "|/usr/local/sbin/cronolog   /path/to/%Y/%m/%d/news" NewsLog env=NewsEnv

这里面加入cronolog是为了把Apache日志自动按年月日归档,方便统计。

现在准备工作基本就做好了,重启一下Apache日志就会按照我们要求的格式记录了,其结果可以入库,然后利用SQL来分析就方便多了,比如“SELECT COUNT(*) ...”就是总的PV数,而“SELECT DISTINCT ip ...”就是IP数,是不是方便多了,那这些日志记录怎么入库呢?是不是要先用PHP来分析日志文件,然后进行字符串处理组成SQL再入库?当然不是,那样的话这篇文章也就没有意义了,要说如何入库,还要先返回头来看看前面我们记录的日志格式:

LogFormat ""%h","%U"" NewsLog

如此记录是有原因的,这是因为MySQL本身有一个“LOAD DATA INFILE”语法,它可以把符合格式的文本数据存入数据库,而其速度是相当高的,单纯的INSERT根本没法和它比。相应的,MySQL还提供了一个mysqlimport工具来实现命令行端的“LOAD DATA INFILE”语法,这样,我们就可以直接编写shell脚本用mysqlimport来完成日志的入库了,跳过了PHP分析日志的步骤,从而提高的效率。随便设定一下crontab,在系统最轻闲的时候,比如凌晨,跑一下前一天的数据入库,哈哈,苦难结束了!

回过头来,解决方法并没有新奇之处,只是我们的头脑里始终有一个惯性思维:Apache日志应该用PHP或者PERL什么的分析一下,然后再存入MySQL库,却不料这样反而割裂的Apache和MySQL的关系。

注意:

在大量数据入库的时候应该先在全部数据入库后再建索引,这样可以插入数据的提高速度。

其实也可以不入库,只是使用shell分析(wc -l, sort -nr, uniq -c...)

总结:

这里说的解决思路主要适用于非即时数据的统计,如果你要的是即时数据,那么上面的是否适合你呢?答案是要看你的即时性要求有多强,如果可以允许一小时一更新的话,那我们就可以把Apache的日志文件利用cronolog设定成每小时一个,然后每小时执行一次cron来更新数据,如果必须是绝对意义上的即时数据,那上面的方法就不适合你了,不过其实解决方法也很简单,只要你明确了瓶颈在哪里就容易了,在这样的问题背景中,系统瓶颈无疑是在大访问量下无法承受的数据库,只要我们能降低它的负载问题就迎刃而解了。最常见的做法是利用memcached这样的缓存系统,然后在更新PV的时候,不是直接更新数据库,而是先更新缓存,当缓存里的计数达到一个指定的数值后,再统一完成一次数据库更新,比如说,某篇新闻在memcached里的访问记录数达到50我们才做一次数据库的更新,同时把memcached里的计数清零,这样的话,数据库的负载就降低为原来的1/50,世界又变得美好了。

不过这里有一个小瑕疵,就是如果我们硬性的规定缓存计数达到50才更新一次数据库的话,那么当我们浏览新闻的时候,会发现新闻的PV都是一些类似“50, 100, 150...”这样的50的倍数,会显得不真实。所以,在设定memcached里计数达到多大才更新数据库时,一般选择一个随机数,比如,每次判断是否更新数据库的时候,先产生一个50~100之间的随机数,然后再判断memcached里的计数是否大于这个随机数,是的话就更新数据库,否的话就更新缓存。这样PV就会是一个很自然的数了。如果你想得到即时的数据,只要把数据库中的计数和缓存中的计数相加就可以了。

今天先说到这。^_^

补充:今天在MySQL看到了类似的东西,链接

3.8. 与Apache一起使用MySQL

还有一些项目,你可以从MySQL数据库鉴别用户,并且你还可以将日志文件写入MySQL数据库表。

你可以将以下内容放到Apache配置文件中,更改Apache日志格式,使MySQL更容易读取:

LogFormat

""%h",%{%Y%m%d%H%M%S}t,%>s,"%b","%{Content-Type}o",

"%U","%{Referer}i","%{User-Agent}i""

要想将该格式的日志文件装载到MySQL,你可以使用以下语句:

LOAD DATA INFILE '/local/access_log' INTO TABLE tbl_name

FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' ESCAPED BY ''

所创建的表中的列应与写入日志文件的LogFormat行对应。

声明: 此文观点不代表本站立场;转载须要保留原文链接;版权疑问请联系我们。