Php 为什么我的全xml站点地图包含50000个链接,而不是最后的额外页面?

Php 为什么我的全xml站点地图包含50000个链接,而不是最后的额外页面?,php,mysqli,mariadb,sitemap,Php,Mysqli,Mariadb,Sitemap,我正在尝试每天使用php+mysqli构建站点地图 当我检查谷歌提交的站点地图时,我所能看到的是每个提交的站点地图中都有50k个URL。每天3k-4k的新帖子被添加到数据库中,所以应该有最后一个带有额外URL的站点地图。我做错了什么 站点地图生成逻辑 表中有一些非活动记录,我不希望它们包含在站点地图中,所以我将它们标记为 enabled = '0' 选择COUNT*作为链接的总计数 选择COUNT*作为启用链接的总计数='1' 我正在尝试构建站点地图,而不让服务器在一次查询中查询4 mil条记

我正在尝试每天使用php+mysqli构建站点地图

当我检查谷歌提交的站点地图时,我所能看到的是每个提交的站点地图中都有50k个URL。每天3k-4k的新帖子被添加到数据库中,所以应该有最后一个带有额外URL的站点地图。我做错了什么

站点地图生成逻辑

表中有一些非活动记录,我不希望它们包含在站点地图中,所以我将它们标记为

enabled = '0'
选择COUNT*作为链接的总计数

选择COUNT*作为启用链接的总计数='1'

我正在尝试构建站点地图,而不让服务器在一次查询中查询4 mil条记录时无响应,然后使用数组块分割结果

更新1:

正如瑞克·詹姆斯所说

从积极的方面来看,我很高兴看到tpb_id订购的id>美元 ASC限制0,$subset\u count-这使得获取块非常有效。 哦,等等;这是无效的-为什么id与tpb_id相对?自从 tpb_id是唯一的,去掉id,将tpb_id提升为唯一的 主键。然后使用tpb_id代替SELECT中的id

我做了更改并运行了站点地图脚本,但最后一个站点地图仍然有精确的50k URL。 在进一步检查时,我注意到站点地图第82页是最后一个站点地图页面,我得到了tpb_id为4188464的最后一条记录

如果我这样做

SELECT * FROM links WHERE tpb_id <= '4188464' AND enabled = '1' 
这意味着所有站点地图页面只包含来自数据库的334564条记录,而不是4mil+记录

在进一步的挖掘中,我发现站点地图页面1-65包含相同的50k URL

这是因为最低tpb_id为3211594

更新2:Rick James建议的未经修改的更改

我用两种类型的查询做了一些测试。这个很慢

类型1:旧的mysql查询

它能够按预期获取记录

类型2:新的快速查询

上面您可以看到类型2给出了相同的输出,因为

tpb_id >  5
tbp_id不是增量的,而是有差距的

结论: 使用使用附加条件的类型2查询时。 若表中有自动递增字段,那个么我们可以使用它作为参考,使用类型2查询来获取更多记录,这是非常快速的

我要查找的最后一个查询是no,orderby/no,其中enabled=1条件

$get_mysql_data_query = " SELECT tpb_id, slug, enabled FROM links WHERE id > $from LIMIT 0 , $subset_count";
然后仅使用那些已启用=1的

while ($row = mysqli_fetch_assoc($result)) {
    if($row['enabled'] == 1){
        $link = 'https://example.com/post-id/'.$row['tpb_id'].'/'.$row['slug'];
    }
}
所以现在,它将使用id作为其增量引用,并且不会有任何间隙,它将扫描所有4mil+记录,但只创建那些已启用的站点地图

我已经按照上面所述更新了脚本,并将在脚本完成运行后进行更新

更新:该脚本运行良好,制作了所有页面的站点地图,并不是所有的站点地图页面都包含50k,因为有许多enabled=0记录,但只要所有URL都包含在站点地图中就可以了

这里是最后的脚本逻辑

<?php
    $subset_count = 50000 ;

    $total_count_query = "SELECT COUNT(*) as total_count FROM links" ;

    $total_count = mysqli_query ($conn, $total_count_query);
    $total_count = mysqli_fetch_assoc($total_count);
    $total_count = $total_count['total_count'];
    $total_pages = ceil ($total_count / $subset_count);

    $current_page = 1;    
    while($current_page <= $total_pages){
        $from = ($current_page * $subset_count) - $subset_count;

        $get_mysql_data_query = " SELECT tpb_id, slug, enabled FROM links WHERE id > $from LIMIT 0 , $subset_count";

        if ($result = mysqli_query($conn, $get_mysql_data_query)) {
            while ($row = mysqli_fetch_assoc($result)) {
                if($row['enabled'] == 1){
                    $link = 'https://example.com/post-id/'.$row['tpb_id'].'/'.$row['slug'];
                }
            }

            mysqli_free_result($result);
        }
        $current_page++ ;
    }
试试这个:

<?php

$perPage = 50000;
$identifier = 0;

while (false !== $identifier) {
    $sql = sprintf(
        "SELECT tpb_id FROM links WHERE enabled = '1' AND tpb_id > %d ORDER BY tpb_id ASC",
        $identifier
    );

    $result = mysqli_query($connection, $sql);
    $rows = mysqli_fetch_assoc($result);

    $identifiers = array_column($rows, 'tpb_id');

    $links = array_map(function ($id) {
        return sprintf(
            'https://example.com/post-id/%s'.
            $id
        );
    }, $identifiers);

    // now, write sitemap 

    $identifier = end($identifiers);
}
从:

如果要列出超过50000个URL,必须创建多个站点地图文件

这意味着,根据您拥有的条目数量,您需要创建多个站点地图文件。您需要创建一个索引文件,以引用将所有条目分块为50000个块所需的尽可能多的站点地图文件,而不是单个站点地图文件

现在,将所有行保留在内存中以创建这些文件可能会使内存崩溃。因此,您可以使用PHP中的数据库驱动程序(例如Mysqli或PDO)所支持的可遍历项,而不是使用数组作为结果集。它们处理每个条目,并且可以从数据库服务器流式传输,因此这对内存更加友好,并且通常只需要很少的运行时开销,或者至少尽可能少地避免出现内存问题

举个例子。假设来自表示结果集的数据库的结果命名为

$result
和一个可遍历的。当您想将其分块为50000个条目时,首先将其扭曲为一个NoRewind迭代器:

$chunkable = new NoRewindIterator($result);
这现在允许foreach超过$chunkable多次,而不需要回放结果集:

$chunkSize = 50000;
while ($chunkable->valid()) {
    foreach (new LimitIterator($chunkable, 0, $chunkSize) as $row) {
        ...
    }
}
这里,LimitIterator用于创建$chunkSize条目的块。foreach将在最多50000个条目之后完成

while循环条件确保整个迭代器仍然有效,如果有效,则启动下一个foreach循环

该示例有点冗长,最好将其包装在生成器中,以便更易于使用:

function chunk(Traversable $traversable, int $size)
{
    $iterator = new IteratorIterator($traversable);
    $chunkable = new NoRewindIterator($iterator);
    # note: some Iterators need a rewind() to have valid() working, so
    #       in this example a "for" instead of a "while" loop is used.
    for ($iterator->rewind(); $chunkable->valid();) {
        $chunk = new LimitIterator($chunkable, 0, $size);
        yield $chunk;
    }
}


foreach (chunk($result, 50000) as $chunk) {
    # new sitemap

    foreach ($chunk as $row) {
        # ...
    }
}
当您一块接一块地构建站点地图块时,这对内存更加友好。对于索引文件,您只需跟踪您创建的文件。

健全性检查

谁将看到400万个链接?甚至一页就有50000个

哪种浏览器可以在合理的时间长度内加载包含50000个链接的页面

如果你必须构建一个包含4M个链接的页面,那么在其中加入一些结构——按字母顺序、分类或其他逻辑分组,而不是盲目的N

妈妈呢 ke每页的大小远远小于50K项。并将其分为两个层次。比如说,你有3个等级;然后每页可以有几百个项目;这对于用户和PHP来说都是可管理的


从积极的方面来看,我很高兴看到id>ORDER BY tpb_id ASC LIMIT 0,$subset_count的$from ORDER,这使得获取块非常有效。哦,等等;这是无效的-为什么id与tpb_id相对?因为tpb_id是唯一的,所以去掉id并将tpb_id提升为主键。然后使用tpb_id代替选择中的id。

是的,但我有大约4 mil记录,它不会消耗所有内存并给我内存错误吗?谢谢你调整了我的建议!那么,我应该在一次查询中读取数据库中的所有记录,并以$result的形式获取数据吗?是的,您可以这样做。这还将确保您可以在一个事务中创建站点地图。从技术上讲,您不会一次获得所有数据,但您会得到表示所有数据的结果集。这是一个细微的区别,它允许您不将整个结果集转换为数组。我删除了列id,并将tpb_id作为主键,在查询中将id更改为tpb_id,运行了脚本,但我的上一个站点地图仍然包含50k URL。有些地方仍然不正确。这是因为tpb_id不是增量的吗?在第82页,我得到了tpb_id为4188464的最后一条记录,如果我从链接中选择*,其中tpb_id如果$FROM现在是上一页的最后一条tpb_id,则每页将获得50K。你的意思是>=,而不是挖掘更多,站点地图页面1-65包含相同的50k链接。因为最低的tpb_id是3211594
while ($row = mysqli_fetch_assoc($result)) {
    if($row['enabled'] == 1){
        $link = 'https://example.com/post-id/'.$row['tpb_id'].'/'.$row['slug'];
    }
}
<?php
    $subset_count = 50000 ;

    $total_count_query = "SELECT COUNT(*) as total_count FROM links" ;

    $total_count = mysqli_query ($conn, $total_count_query);
    $total_count = mysqli_fetch_assoc($total_count);
    $total_count = $total_count['total_count'];
    $total_pages = ceil ($total_count / $subset_count);

    $current_page = 1;    
    while($current_page <= $total_pages){
        $from = ($current_page * $subset_count) - $subset_count;

        $get_mysql_data_query = " SELECT tpb_id, slug, enabled FROM links WHERE id > $from LIMIT 0 , $subset_count";

        if ($result = mysqli_query($conn, $get_mysql_data_query)) {
            while ($row = mysqli_fetch_assoc($result)) {
                if($row['enabled'] == 1){
                    $link = 'https://example.com/post-id/'.$row['tpb_id'].'/'.$row['slug'];
                }
            }

            mysqli_free_result($result);
        }
        $current_page++ ;
    }
<?php

$perPage = 50000;
$identifier = 0;

while (false !== $identifier) {
    $sql = sprintf(
        "SELECT tpb_id FROM links WHERE enabled = '1' AND tpb_id > %d ORDER BY tpb_id ASC",
        $identifier
    );

    $result = mysqli_query($connection, $sql);
    $rows = mysqli_fetch_assoc($result);

    $identifiers = array_column($rows, 'tpb_id');

    $links = array_map(function ($id) {
        return sprintf(
            'https://example.com/post-id/%s'.
            $id
        );
    }, $identifiers);

    // now, write sitemap 

    $identifier = end($identifiers);
}
$result
$chunkable = new NoRewindIterator($result);
$chunkSize = 50000;
while ($chunkable->valid()) {
    foreach (new LimitIterator($chunkable, 0, $chunkSize) as $row) {
        ...
    }
}
function chunk(Traversable $traversable, int $size)
{
    $iterator = new IteratorIterator($traversable);
    $chunkable = new NoRewindIterator($iterator);
    # note: some Iterators need a rewind() to have valid() working, so
    #       in this example a "for" instead of a "while" loop is used.
    for ($iterator->rewind(); $chunkable->valid();) {
        $chunk = new LimitIterator($chunkable, 0, $size);
        yield $chunk;
    }
}


foreach (chunk($result, 50000) as $chunk) {
    # new sitemap

    foreach ($chunk as $row) {
        # ...
    }
}