PHP脚本逐渐变慢(文件阅读器)

时间:2010-08-15 10:05:15

标签: php fopen fgets slowdown

我有一个脚本,当针对计时器时,会逐渐变慢。这很简单,因为它只是读取一行,检查它然后将它添加到数据库,然后进入下一行。

这是它的输出逐渐恶化:

Record: #1,001 Memory: 1,355,360kb taking 1.84s
Record: #1,001 Memory: 1,355,360kb taking 1.84s
Record: #2,002 Memory: 1,355,192kb taking 2.12s
Record: #3,003 Memory: 1,355,192kb taking 2.39s
Record: #4,004 Memory: 1,355,192kb taking 2.65s
Record: #5,005 Memory: 1,355,200kb taking 2.94s
Record: #6,006 Memory: 1,355,376kb taking 3.28s
Record: #7,007 Memory: 1,355,176kb taking 3.56s
Record: #8,008 Memory: 1,355,408kb taking 3.81s
Record: #9,009 Memory: 1,355,464kb taking 4.07s
Record: #10,010 Memory: 1,355,392kb taking 4.32s
Record: #11,011 Memory: 1,355,352kb taking 4.63s
Record: #12,012 Memory: 1,355,376kb taking 4.90s
Record: #13,013 Memory: 1,355,200kb taking 5.14s
Record: #14,014 Memory: 1,355,184kb taking 5.43s
Record: #15,015 Memory: 1,355,344kb taking 5.72s

不幸的是,该文件约为20gb,所以当整个事物以增加的速度读取时,我可能已经死了。代码(主要)在下面,但我怀疑它与fgets()有关,但我不确定是什么。

    $handle = fopen ($import_file, 'r');

    while ($line = fgets ($handle))
    {
        $data = json_decode ($line);

        save_record ($data, $line);
    }

提前致谢!

修改

评论'save_record($ data,$ line);'似乎什么都不做。

4 个答案:

答案 0 :(得分:1)

有时最好使用系统命令来读取这些大文件。我碰到了类似的东西,这是我用过的一个小技巧:

$lines = exec("wc -l $filename");
for($i=1; $i <= $lines; $i++) {
   $line = exec('sed \''.$i.'!d\' '.$filename);

   // do what you want with the record here
}

我不推荐使用不可信任的文件,但它运行速度很快,因为它使用系统一次拉一条记录。希望这会有所帮助。

答案 1 :(得分:0)

http://php.net/manual/en/function.fgets.php

根据Leigh Purdie的评论,对fgets的大文件存在一些性能问题。如果你的JSON对象比他的测试行大,你的限制可能会更快

使用http://php.net/manual/en/function.stream-get-line.php并指定长度限制

答案 2 :(得分:0)

好吧,性能问题。显然,当它不应该,或者更重要的是,某些事情应该是恒定时间似乎与目前处理的记录数量呈线性关系。第一个问题是展示问题的最小代码片段是什么。我想知道当你注释掉所有但是逐行读取文件时,你是否会遇到同样的问题行为。如果是这样,那么你将需要一种没有这个问题的语言。 (有很多。)无论如何,一旦你看到预期的时间特征,一个接一个地添加语句,直到你的时间变得混乱,你就会发现问题。

你指示了某些东西或其他东西来获得时间。通过单独执行15000次左右,确保这些不会导致问题。

答案 3 :(得分:0)

我试图找到一种让我更快速通过96G文本文件的方法时发现了这个问题。我最初写的剧本花了15个小时达到0.1%...

我已经尝试了一些这里建议的解决方案,使用stream_get_line,fgets和exec for sed。我最后采用了一种不同的方法,我认为我会与其他任何人分享这个问题。

拆分文件! : - )

在我的freebsd框中(也存在于linux等)我有一个名为'split'的命令行实用程序。

usage: split [-l line_count] [-a suffix_length] [file [prefix]]
       split -b byte_count[K|k|M|m|G|g] [-a suffix_length] [file [prefix]]
       split -n chunk_count [-a suffix_length] [file [prefix]]
       split -p pattern [-a suffix_length] [file [prefix]]

所以我跑了:

split -l 25000 -a 3 /data/var/myfile.log /data/var/myfile-log/

然后我在/ data / var / myfile-log /目录中找到5608个文件,然后可以使用以下命令对所有文件进行处理:

php -f do-some-work.php /data/var/myfile-log/*