Php 为什么我的全xml站点地图包含50000个链接,而不是最后的额外页面?
我正在尝试每天使用php+mysqli构建站点地图 当我检查谷歌提交的站点地图时,我所能看到的是每个提交的站点地图中都有50k个URL。每天3k-4k的新帖子被添加到数据库中,所以应该有最后一个带有额外URL的站点地图。我做错了什么 站点地图生成逻辑 表中有一些非活动记录,我不希望它们包含在站点地图中,所以我将它们标记为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条记
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) {
# ...
}
}