我正在尝试使用php + mysqli(mariadb)
每天构建站点地图当我检查谷歌提交的站点地图时,我所能看到的是每个已提交的站点地图都有50k的网址。每天3k-4k的新帖子会被添加到数据库中,因此应该有最后一个带有额外URL的站点地图。我做错了什么?
站点地图生成逻辑
<?php
$subset_count = 50000 ;
$total_count_query = "SELECT COUNT(*) as total_count FROM links WHERE enabled = '1' " ;
$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 FROM links WHERE enabled = '1' ORDER BY tpb_id ASC LIMIT $from , $subset_count"; // this is slow query
$get_mysql_data_query = " SELECT tpb_id, slug FROM links WHERE enabled = '1' AND id > $from ORDER BY tpb_id ASC LIMIT 0 , $subset_count";
if ($result = mysqli_query($conn, $get_mysql_data_query)) {
while ($row = mysqli_fetch_assoc($result)) {
$link = 'https://example.com/post-id/'.$row['tpb_id'].'/'.$row['slug'];
}
mysqli_free_result($result);
}
$current_page++ ;
}
我的表架构
MariaDB [Db]> describe links;
+---------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| tpb_id | int(11) | NO | UNI | 0 | |
| slug | varchar(255) | NO | | | |
| enabled | tinyint(1) | NO | | 1 | |
+---------+--------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)
表中有一些inactive records
,我不希望它们包含在站点地图中,所以我将它们标记为
enabled = '0'
SELECT COUNT(*)as total_count FROM links;
+-------------+
| total_count |
+-------------+
| 4162840 |
+-------------+
1 row in set (0.00 sec)
SELECT COUNT(*)as total_count FROM links WHERE enabled =&#39; 1&#39;;
+-------------+
| total_count |
+-------------+
| 4053924 |
+-------------+
1 row in set (0.91 sec)
我正在尝试构建站点地图而不会让服务器无法响应购买在一个查询中查询4 mil记录然后使用数组块来分割结果。
更新1:
正如Rick James所说,
从积极的方面来说,我很高兴看到
id > $from ORDER BY tpb_id ASC LIMIT 0 , $subset_count
- 这使得获取块有效。 等一下;效率不高 - 为什么id
与tpb_id
?以来tpb_id
为UNIQUE
,摆脱id
并宣传tpb_id
为PRIMARY KEY
。然后使用tpb_id
代替id
中的SELECT
。
我做了更改并运行了站点地图脚本,但最后一个站点地图仍有50k个网址。
在进一步检查时,我注意到站点地图第82页,这是最后一个站点地图页面,我得到的最后一条记录,其tpb_id为4188464
,
如果我这样做
SELECT * FROM links WHERE tpb_id <= '4188464' AND enabled = '1'
然后我得到了
334,564 rows
这意味着所有站点地图页面都只包含数据库中的334,564
条记录,而不是4mil+
条记录。
进一步挖掘我发现,网站地图网页1-65
包含相同的50k urls
。
之所以发生这种情况,是因为最低tpb_id
为3211594
更新2:Rick James建议的无法更改,
我用两种类型的查询做了一些测试。 (这个很慢)
类型1:旧的mysql查询
MariaDB [db_name]> SELECT * FROM links ORDER BY tpb_id ASC LIMIT 0 , 5 ;
+----+---------+--------------------------------------------------+---------+
| id | tpb_id | slug | enabled |
+----+---------+--------------------------------------------------+---------+
| 1 | 3211594 | High.Chaparall.S02E02.PDTV.XViD.SWEDiSH-HuBBaTiX | 1 |
| 2 | 3211609 | School.Of.Rock.PROPER.DVDRip.XviD-DMT | 1 |
| 3 | 3211623 | Gyllene_Tider-Samtliga_Hits-SE-2004-WLM | 1 |
| 4 | 3211625 | Oz_-_Season_One | 1 |
| 5 | 3211626 | Prince_of_Persia_-_XBOX | 1 |
+----+---------+--------------------------------------------------+---------+
5 rows in set (0.01 sec)
MariaDB [db_name]> SELECT * FROM links ORDER BY tpb_id ASC LIMIT 5 , 5 ;
+----+---------+-------------------------------------------+---------+
| id | tpb_id | slug | enabled |
+----+---------+-------------------------------------------+---------+
| 6 | 3211629 | Welcome.To.Mooseport.DVDSCR.XViD-DVL | 1 |
| 7 | 3211639 | Top_Secret_Recipes_E-books | 1 |
| 8 | 3211687 | Italian_Teens_Home_Video | 1 |
| 9 | 3211706 | Splinter_Cell_-_PandoraTomorrow_-DEViANCE | 1 |
| 10 | 3211770 | Revolution_OS | 1 |
+----+---------+-------------------------------------------+---------+
5 rows in set (0.00 sec)
它能够按预期获取记录。
类型2:新的快速查询。
MariaDB [db_name]> SELECT * FROM links WHERE enabled = '1' AND tpb_id > 0 ORDER BY tpb_id ASC LIMIT 0 , 5 ;
+----+---------+--------------------------------------------------+---------+
| id | tpb_id | slug | enabled |
+----+---------+--------------------------------------------------+---------+
| 1 | 3211594 | High.Chaparall.S02E02.PDTV.XViD.SWEDiSH-HuBBaTiX | 1 |
| 2 | 3211609 | School.Of.Rock.PROPER.DVDRip.XviD-DMT | 1 |
| 3 | 3211623 | Gyllene_Tider-Samtliga_Hits-SE-2004-WLM | 1 |
| 4 | 3211625 | Oz_-_Season_One | 1 |
| 5 | 3211626 | Prince_of_Persia_-_XBOX | 1 |
+----+---------+--------------------------------------------------+---------+
5 rows in set (0.00 sec)
MariaDB [db_name]> SELECT * FROM links WHERE enabled = '1' AND tpb_id > 5 ORDER BY tpb_id ASC LIMIT 0 , 5 ;
+----+---------+--------------------------------------------------+---------+
| id | tpb_id | slug | enabled |
+----+---------+--------------------------------------------------+---------+
| 1 | 3211594 | High.Chaparall.S02E02.PDTV.XViD.SWEDiSH-HuBBaTiX | 1 |
| 2 | 3211609 | School.Of.Rock.PROPER.DVDRip.XviD-DMT | 1 |
| 3 | 3211623 | Gyllene_Tider-Samtliga_Hits-SE-2004-WLM | 1 |
| 4 | 3211625 | Oz_-_Season_One | 1 |
| 5 | 3211626 | Prince_of_Persia_-_XBOX | 1 |
+----+---------+--------------------------------------------------+---------+
5 rows in set (0.01 sec)
上面的你可以看到类型2由于
而提供相同的输出tpb_id > 5
和tbp_id没有增量,并且存在差距。
结论:
使用类型2查询时使用附加条件。
如果表格有Auto Increment
字段,那么我们可以使用它作为参考,以便使用type 2
查询来获取更多记录。
所以我正在寻找的最终查询(不,按顺序排序/否,启用= 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记录,但只要所有网址都包含在站点地图中,就可以了。
这是最终的脚本逻辑。
<?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++ ;
}
答案 0 :(得分:5)
如果要列出超过50,000个网址,则必须创建多个Sitemap文件。
这意味着根据您拥有的条目数量,您需要创建多个站点地图文件。您需要创建一个引用尽可能多的站点地图文件的索引文件,而不是单个站点地图文件,您需要将所有条目分成5万个块。
现在将所有行保留在内存中以创建这些文件可能会使内存崩溃。因此,您可以使用PHP中的数据库驱动程序(例如Mysqli或PDO)支持的可遍历,而不是使用数组作为结果集。这些工作在每个条目上,并且可以从数据库服务器流式传输,因此这对内存更友好(并且通常只有很少的运行时间开销 - 或者至少在不遇到内存问题的情况下尽可能少)
举个例子。假设数据库中表示结果集的结果名为
$result
和 Traversable 。如果你想把它分成5万个条目,你首先将它变成一个 NoRewind 迭代器:
$chunkable = new NoRewindIterator($result);
现在允许foreach
多次$chunkable
多次{/ 1}}无需倒带结果集:
$chunkSize = 50000;
while ($chunkable->valid()) {
foreach (new LimitIterator($chunkable, 0, $chunkSize) as $row) {
...
}
}
此处 LimitIterator 用于创建$chunkSize
个条目块。 foreach将在最多5万个条目之后完成。
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) {
# ...
}
}
当你在另一个之后构建一个站点地图块时,这对内存更友好了。对于索引文件,您只需跟踪您创建的文件。
答案 1 :(得分:2)
善意检查!
谁将会看到400万个链接?甚至单个页面上还有50,000个?
哪种浏览器可以在合理的时间内加载包含50,000个链接的页面?
如果你必须用4M链接构建一堆页面,请在其中加入一些结构 - 按字母顺序排序或分类或其他逻辑分组,而不是盲目N.
并使每页的大小远小于50K项。并使其达到两个层次。说,你有3个级别;然后每个“页面”可能是几百个项目;这对于PHP的用户和是可管理的。
从积极的方面来说,我很高兴看到$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >0) {
$(".left-nav").toggleClass("white-nav");
}
else{
$(".left-nav").toggleClass("white-nav");
}
});
- 这使得获取块有效。等一下;效率不高 - 为什么id > $from ORDER BY tpb_id ASC LIMIT 0 , $subset_count
与id
?由于tpb_id
为tpb_id
,因此请删除UNIQUE
并将id
提升为tpb_id
。然后使用PRIMARY KEY
代替tpb_id
中的id
。
答案 2 :(得分:0)
试试这个:
<?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);
}