【问题标题】:Generating an XML file with large amounts of data and avoid memory cap生成包含大量数据的 XML 文件并避免内存上限
【发布时间】:2012-11-12 05:29:31
【问题描述】:

我现在有一个包含大约 300k 页面的 Wordpress 站点,以及一个内存为 1GB 的服务器。不幸的是,所有的站点地图生成插件都无法处理它。我尝试了 3 种不同的使用 PHP 写入 XML 的方法(XMLWriter、SimpleXMLElement 和 DOMDocument),它们最终都限制在大约 30k 页(xml 节点)。

你认为我能做些什么来完成这项工作?最坏的情况是,我考虑过设置多个 cron 作业,每天每十分钟运行一次,并继续打开/附加到文件,然后分块添加,但这显然不是最佳解决方案。我发现一些 sn-p 声称能够在我的循环期间清除内存,但它也没有起到作用。这是该 sn-p 的示例:

$xml = '<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">';
for ($i = 0 ; $i< 20; $i++) {
  $query = mysql_query(sprintf("SELECT ID, post_date FROM wp_posts WHERE post_status='publish' LIMIT %s,%s", $i*10000, 10000));
  while ($row = mysql_fetch_array($query)) {
    $xml .= '<url>';
    $xml .= '<loc>'.get_permalink($row['ID']).'</loc>';
    $xml .= '<lastmod>'.$row['post_date'].'</lastmod>';
    $xml .= '<changefreq>weekly</changefreq>';
    $xml .= '<priority>0.6</priority>';
    $xml .= '</url>';
  }
}
$xml .= '</urlset>';

$sxe = new SimpleXMLElement($xml);
$sxe->asXML("sitemap.xml");

【问题讨论】:

    标签: php xml wordpress


    【解决方案1】:

    你为什么要一次抓取所有记录?

    尝试为每个请求获取 10000 行。并在每次迭代后清理内存。

    如果你在cli模式下运行旧版本的php不会释放内存,所以你可以尝试forkhttp://php.net/manual/en/function.pcntl-fork.php

    怎么做:

    1. 无需使用任何 xml 库,sprintf 就可以解决问题。
    2. 将其包装成 for ($i = 0, $i
    3. 查询看起来像LIMIT ($i*10000) 10000

    代码示例:

    for ($i = 0 ; $i< 5 $i++) {
        ...
        $sth = $dbh->prepare('SELECT * FROM table_name LIMIT ? ?');
        $sth->execute(array($i*10000, 10000));
        ...
    }
    

    另一个代码示例:

        <?php
        $fileHandle = fopen("sitemap.xml", "w");
    
        fwrite($fileHandle,
            '<?xml version="1.0" encoding="UTF-8"?>' . 
            '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"' .
            ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' .
            ' xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9' .
            ' http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">'
        );
    
        for ($i = 0 ; $i< 20; $i++) {
            $query = mysql_query(sprintf("SELECT ID, post_date FROM wp_posts WHERE post_status='publish' LIMIT %s,%s", $i*10000, 10000));
            $xml = '';
            while ($row = mysql_fetch_array($query)) {
                $xml .= '<url>'.
                    '<loc>'.get_permalink($row['ID']).'</loc>' .
                    '<lastmod>'.$row['post_date'].'</lastmod>' .
                    '<changefreq>weekly</changefreq>' .
                    '<priority>0.6</priority>' .
                    '</url>';
            }
    
            fwrite($fileHandle, $xml);
        }
        fwrite($fileHandle, '</urlset>');
    
        fclose($fileHandle);
    
        echo PHP_EOL . 'memory used: '. memory_get_peak_usage() . PHP_EOL;
    

    【讨论】:

    • 我对处理这种规模的数据有点陌生。我知道我可以限制我提取的记录数量,但我如何告诉它按照您的建议进行操作? (如拉入 10,000、清除内存、拉入下一个 10,000 等等,都在同一个脚本中)。谢谢! :)
    • 好的,尝试使 mysql 查询的语法正确。它不喜欢我用for ($i = 0; $i &lt; 5; $i++) { $query = mysql_query("SELECT ID, post_date FROM wp_posts WHERE post_status='publish' LIMIT ($i*10000) 10000") 放在那里的东西
    • 抱歉,第一件事是将 mysql_* 替换为 php.net/manual/en/pdo.prepare.php,因为 mysql_* 已被贬低。
    • 用示例更新帖子(PDO)。如果不想改成pdo,使用mysql_query(sprintf('SELECT * FROM table LIMIT %s %s', $i*10000, 10000))
    • 真棒谢谢你,效果很好(只需要 %s 和 %s 之间的逗号)。听起来我有很多东西要学,我什至没有意识到 mysql_ 已被弃用。我现在正在使用它,但想改变它。您知道任何用于进行这种转换的好资源吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    • 2011-06-27
    • 2021-11-13
    • 2016-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多