导入大型xml订阅源(LAMP)时出现问题

时间:2009-10-27 22:10:37

标签: php xml regex cron

想知道是否有人可以帮助我解决我遇到的一个小问题

问题是负载可以达到5,并且CPU使用率可以达到40%,在具有356Mb RAM的双核“Xeon L5410 @ 2.33GHz”上,我不知道我应该在哪里调整代码以及防止这种情况的方法。下面的代码示例

//注意$ productFile可以是40Mb .gz压缩,700Mb未压缩(xml文本文件)    if(file_exists($ productFile)){

  $fResponse = gzopen($productFile, "r");
  if ($fResponse) {

     while (!gzeof($fResponse)) {

        $sResponse = "";
        $chunkSize = 10000;
        while (!gzeof($fResponse) && (strlen($sResponse) < $chunkSize)) {
          $sResponse .= gzgets($fResponse, 4096);
        }
        $new_page .= $sResponse;
        $sResponse = "";
        $thisOffset = 0;
        unset($matches);

        if (strlen($new_page) > 0) {

           //Emptying
           if (!(strstr($new_page, "<product "))) {
              $new_page = "";
           }

           while (preg_match("/<product [^>]*>.*<\/product>/Uis", $new_page, $matches, PREG_OFFSET_CAPTURE, $thisOffset)) {

              $thisOffset = $matches[0][1];
              $thisLength = strlen($matches[0][0]);
              $thisOffset = $thisOffset + $thisLength;

              $new_page   = substr($new_page, $thisOffset-1);
              $thisOffset = 0;

              $new_page_match = $matches[0][0];

              //- Save collected data here -//

              }

           }//End while loop


        }

     }
     gzclose($fResponse);
  }

}

$ chunkSize - 它应该尽可能小以减少内存使用量并简化正则表达式,或者它应该更大以避免代码耗时太长而无法运行。

40,000匹配负载/ CPU峰值。所以,有人对如何通过crons管理大型Feed上传有任何建议。

提前感谢您的帮助

4 个答案:

答案 0 :(得分:1)

你至少有两个问题。首先是您尝试将整个700 MB文件解压缩到内存中。事实上,你这样做了两次。

while (!gzeof($fResponse) && (strlen($sResponse) < $chunkSize)) {
    $sResponse .= gzgets($fResponse, 4096);
}
$new_page .= $sResponse;

$sResponse$new_page都将包含一个字符串,该字符串将包含整个700 MB文件。因此,当脚本运行时,你正在吃掉1.4 GB的内存,更不用说字符串连接的成本了(虽然PHP处理字符串比其他语言更好,但是mutable和non-mutable会有什么限制你)

第二个问题是你在$new_page中越来越大的字符串上运行正则表达式。随着$new_page越来越大,这将增加服务器的负担。

解决问题的最简单方法是分割任务。

  1. 在进行任何处理之前,将整个文件解压缩到磁盘。

  2. 使用基于 steram 的XML解析器,例如XMLReader或旧SAX Event Based parser

  3. 即使使用基于流/事件的解析器,将结果存储在内存中也可能会占用大量内存。在这种情况下,您需要将每个匹配项存储在磁盘/数据库中。

答案 1 :(得分:0)

既然你说你正在使用灯泡,我可以建议回答一个问题:Suggestions/Tricks for Throttling a PHP script

他建议在有问题的脚本上使用nice命令来降低它阻塞服务器的可能性。

另一种方法是分析脚本并查看任何瓶颈。我会推荐xdebug和kcachegrind或者webcachegrind。有无数的问题和网站可以帮助您设置脚本分析。

答案 2 :(得分:0)

您可能还想查看PHP的基于SAX事件的XML解析器 - http://uk3.php.net/manual/en/book.xml.php

这对于解析大型XML文件很有用(我们将它用于与您正在处理的类似大小的XML文件)并且它做得非常好。那么不需要正则表达式:)

在处理文件之前,您需要先解压缩文件。

答案 3 :(得分:0)

艾伦。该脚本永远不会在内存中保存700Mb,因为看起来$sResponse在达到$ chunkSize后立即被清除并且已添加到$ new_page,

$new_page .= $sResponse;
$sResponse = "";
一旦找到每个匹配,

$new_page将被删除,如果没有可能的匹配,则为每个$ chunkSize数据块清除。

$new_page   = substr($new_page, $thisOffset-1);

if (!(strstr($new_page, "<product "))) {
   $new_page = "";
}

虽然我不能说我能看出实际问题所在。