php - 带有 SimpleXMLElement 的大型 PHP for 循环非常慢 : memory issues?

标签 php xml loops mysqli simplexml

我目前有一些 PHP 代码基本上从 xml 文件中提取数据并使用 $products = new SimpleXMLElement($xmlString); 创建简单的 xml 对象然后我循环遍历这段代码一个 for 循环,我在其中为 XML 文档中的每个产品设置产品详细信息。然后将其保存到 mySql 数据库中。

在运行此脚本时,添加的产品频率会降低,直到它们最终在达到最大值之前停止。我试过间歇性地运行垃圾回收,但无济于事。以及取消设置似乎不起作用的各种变量。

部分代码如下所示:

<?php
$servername = "localhost";
$username = "database.database";
$password = "demwke";
$database = "databasename";
$conn = new mysqli($servername, $username, $password, $database);

$file = "large.xml";
$xmlString = file_get_contents($file);
$products = new SimpleXMLElement($xmlString);
unset($xmlString, $file);
$total = count($products->datafeed[0]);

echo 'Starting<br><br>';

for($i=0;$i<$total;$i++){
    $id = $products->datafeed->prod[$i]['id'];
etc etc
    $sql = "INSERT INTO products (id, name, uid, cat, prodName, brand, desc, link, imgurl, price, subcat) VALUES ('$id', '$store', '$storeuid', '$category', '$prodName', '$brand', '$prodDesc', '$link', '$image', '$price', '$subCategory')";
}
echo '<br>Finished';
?>

php 变量都使用与 $id 类似的行定义,但为了便于阅读而被删除。

关于我可以做什么/阅读什么来完成这个有什么想法吗?只要最终完成,花费的时间对我来说并不重要。

最佳答案

更新:永远不要在 SimpleXML 中使用索引,除非您的对象非常少。请改用 foreach:

// Before, with [index]:
for ($i=0;$i<$total;$i++) {
    $id = $products->datafeed->prod[$i]['id'];
    ...

// After, with foreach():
$i = 0;
foreach ($products->datafeed->prod as $prod) {
    $i++; // Remove if you don't actually need $i
    $id = $prod['id'];
    ...

通常,...->node[$i] 将访问数组 node[] 并将其全部读取到所需的索引,以便迭代节点数组不是 o(N),而是 o(N2)。没有解决方法,因为不能保证当您访问项目 K 时,您刚刚访问了项目 K-1(递归地依此类推)。 foreach 保存指针并因此在 o(N) 中工作。

出于同样的原因,使用 foreach 遍历整个数组可能是有利的,即使您真的只需要少数已知项(除非它们很少并且非常接近数组的开头):

    $a[0] = $products->datafeed->prod[15]['id'];
    ...
    $a[35] = $products->datafeed->prod[1293]['id'];

// After, with foreach():
$want = [ 15, ... 1293 ];
$i = 0;
foreach ($products->datafeed->prod as $prod) {
    if (!in_array(++$i, $want)) {
        continue;
    }
    $a[] = $prod['id'];
}

您应该首先验证增加的延迟是由 MySQLi 还是由 XML 处理引起的。您可以从循环中删除(注释掉)SQL 查询执行,而不是其他任何内容,以验证速度(假设它现在会更高......:-))现在是否保持不变,或显示相同的下降。

我怀疑 XML 处理是罪魁祸首,在这里:

for($i=0;$i<$total;$i++){
    $id = $products->datafeed->prod[$i]['id'];

...您可以在其中访问越来越远的索引,进入 SimpleXMLObject。这可能会遇到 Schlemiel the Painter 的问题.

您的问题“无论何时,我如何让循环完成”的直接答案是“增加内存限制和最长执行时间”。

要提高性能,您可以在提要对象中使用不同的界面:

$i = -1;
foreach ($products->datafeed->prod as $prod) {
    $i++;
    $id = $prod['id'];
    ...
}

实验

我用这个小程序读取一个大的 XML 并迭代它的内容:

// Stage 1. Create a large XML.
$xmlString = '<?xml version="1.0" encoding="UTF-8" ?>';
$xmlString .= '<content><package>';
for ($i = 0; $i < 100000; $i++) {
    $xmlString .=  "<entry><id>{$i}</id><text>The quick brown fox did what you would expect</text></entry>";
}
$xmlString .= '</package></content>';

// Stage 2. Load the XML.
$xml    = new SimpleXMLElement($xmlString);

$tick   = microtime(true);
for ($i = 0; $i < 100000; $i++) {
    $id = $xml->package->entry[$i]->id;
    if (0 === ($id % 5000)) {
        $t = microtime(true) - $tick;
        print date("H:i:s") . " id = {$id} at {$t}\n";
        $tick = microtime(true);
    }
}

生成 XML 后,一个循环对其进行解析并打印迭代 5000 个元素需要多少时间。为了验证它确实是时间增量,还打印了日期。增量应该大约是时间戳之间的时间差。

21:22:35 id = 0 at 2.7894973754883E-5
21:22:35 id = 5000 at 0.38135695457458
21:22:38 id = 10000 at 2.9452259540558
21:22:44 id = 15000 at 5.7002019882202
21:22:52 id = 20000 at 8.0867099761963
21:23:02 id = 25000 at 10.477082967758
21:23:15 id = 30000 at 12.81209897995
21:23:30 id = 35000 at 15.120756149292

事情就是这样:处理 XML 数组的速度越来越慢

这基本上是使用 foreach 的同一个程序:

// Stage 1. Create a large XML.
$xmlString = '<?xml version="1.0" encoding="UTF-8" ?>';
$xmlString .= '<content><package>';
for ($i = 0; $i < 100000; $i++) {
    $xmlString .=  "<entry><id>{$i}</id><text>The quick brown fox did ENTRY {$i}.</text></entry>";
}
$xmlString .= '</package></content>';

// Stage 2. Load the XML.
$xml    = new SimpleXMLElement($xmlString);

$i      = 0;
$tick   = microtime(true);
foreach ($xml->package->entry as $data) {
    // $id = $xml->package->entry[$i]->id;
    $id = $data->id;
    $i++;
    if (0 === ($id % 5000)) {
        $t = microtime(true) - $tick;
        print date("H:i:s") . " id = {$id} at {$t} ({$data->text})\n";
        $tick = microtime(true);
    }
}

现在时间似乎是恒定的...我说“似乎”是因为它们似乎减少了大约一万倍,而且我很难获得可靠的测量结果。

(不,我不知道。我可能从未使用过大型 XML 数组的索引)。

21:33:42 id = 0 at 3.0994415283203E-5 (The quick brown fox did ENTRY 0.)
21:33:42 id = 5000 at 0.0065329074859619 (The quick brown fox did ENTRY 5000.)
...
21:33:42 id = 95000 at 0.0065121650695801 (The quick brown fox did ENTRY 95000.)

关于php - 带有 SimpleXMLElement 的大型 PHP for 循环非常慢 : memory issues?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29756088/

相关文章:

java - 如何在java中从xml实体字符串值创建不同的pojo

xml - 用反 XML 替换 XML 节点

c - 如何编写正确的 do..while 循环?

php - Mysql连接类

jquery - 戈朗 : Extracting XML Issue

c++ - 无法理解带有两个变量的循环

python - 我需要一种快速的方法来循环遍历 Python 中图像/堆栈的像素

php - 如何命名异常(PHP)?

php - mysql_close() : supplied argument is not a valid MySQL-Link

javascript - AngularJS 的异步特性与摘要/应用