多RSS条目插入MySQL数据库异常问题排查求助
问题排查与解决方案
核心原因
你的脚本仅插入每个Feed的1条条目,核心问题出在数据库表约束与插入逻辑不匹配:
你在插入时将Feed的标识ID(即每个资源的id字段)赋值给了数据库表的id字段,如果articles表的id是主键或设置了唯一约束,同一个Feed的所有条目都会因为id重复而插入失败,最终只能成功插入每个Feed的第一条数据。
另外脚本还存在两个次要问题:
- 插入循环固定执行1000次,若
$data数组实际条目数不足1000,会触发数组越界错误导致脚本终止; - 未检查数据库插入的执行结果,无法及时发现插入失败的具体原因。
解决方案
1. 修正数据库表结构
如果id字段是主键,建议将其改为自增主键,同时新增feed_id字段存储Feed的标识(即原脚本中的$resource['id']),避免主键冲突。
示例SQL修改:
ALTER TABLE articles MODIFY COLUMN id INT AUTO_INCREMENT PRIMARY KEY; ALTER TABLE articles ADD COLUMN feed_id INT NOT NULL;
2. 调整插入逻辑
- 修改SQL语句,将原
id字段替换为新增的feed_id; - 根据
$data的实际长度循环插入,避免数组越界; - 添加执行结果检查,记录错误信息方便排查。
3. 优化脚本细节
- 变量名
$json改为$xmlDoc,避免混淆(因为加载的是RSS XML而非JSON); - 每次加载新RSS时创建新的DOMDocument实例,避免残留数据;
- 优化预处理语句的使用,减少重复绑定的开销。
修改后的完整脚本
<?php $data = array(); $resources = array( array( 'type' => 'Article', 'source' => 'Source 1', 'feedurl' => 'http://www.example1.com/feed/', 'id' => '1' ), array( 'type' => 'Article', 'source' => 'Source 2', 'feedurl' => 'https://example2.com/feed', 'id' => '2' ), array( 'type' => 'Article', 'source' => 'Source 3', 'feedurl' => 'https://example3.com/feed', 'id' => '3' ) ); foreach ( $resources as $resource ) { // 每次循环创建新的DOMDocument实例 $xmlDoc = new DOMDocument(); // 禁用错误输出,避免RSS格式问题导致脚本终止 libxml_use_internal_errors(true); $loadSuccess = $xmlDoc->load( $resource['feedurl'] ); libxml_clear_errors(); if (!$loadSuccess) { // 记录Feed加载失败的信息 error_log("Failed to load feed: " . $resource['feedurl']); continue; } foreach ( $xmlDoc->getElementsByTagName( 'item' ) as $node ) { // 检查节点是否存在必要字段,避免报错 $titleNode = $node->getElementsByTagName( 'title' )->item( 0 ); $linkNode = $node->getElementsByTagName( 'link' )->item( 0 ); $dateNode = $node->getElementsByTagName( 'pubDate' )->item( 0 ); if (!$titleNode || !$linkNode || !$dateNode) { continue; } $item = array( 'source' => $resource['source'], 'type' => $resource['type'], 'title' => $titleNode->nodeValue, 'link' => $linkNode->nodeValue, 'date' => $dateNode->nodeValue, 'feed_id' => $resource['id'] ); array_push( $data, $item ); } } // 按日期排序 usort( $data, function( $a, $b ) { return strtotime( $b['date'] ) - strtotime( $a['date'] ); }); // 数据库连接 $servername = '???'; $username = '???'; $password = '???'; $dbname = '???'; $DBconnection = new mysqli($servername, $username, $password, $dbname); if (mysqli_connect_errno()) { printf("Connect failed: %s\n", mysqli_connect_error()); exit(); } // 调整SQL语句,使用feed_id替代原id字段 $sql = "INSERT INTO articles(source, type, title, url, date, feed_id) VALUES(?, ?, ?, ?, ?, ?)"; $stmt = $DBconnection->prepare($sql); if (!$stmt) { error_log("Prepare failed: " . $DBconnection->error); $DBconnection->close(); exit(); } // 绑定参数(仅绑定一次) $source = ''; $type = ''; $title = ''; $link = ''; $date = ''; $feed_id = 0; $stmt->bind_param("sssssi", $source, $type, $title, $link, $date, $feed_id); // 计算实际要插入的条目数(最多1000条) $insertCount = min(count($data), 1000); for ( $x = 0; $x < $insertCount; $x++ ) { $source = $data[ $x ]['source']; $type = $data[ $x ]['type']; $title = htmlspecialchars(str_replace( ' & ', ' & ', $data[ $x ]['title'] )); $link = htmlspecialchars($data[ $x ]['link']); $date = date( 'Y-m-d H:i:s', strtotime( $data[ $x ]['date'] ) ); $feed_id = $data[ $x ]['feed_id']; if (!$stmt->execute()) { // 记录插入失败的信息 error_log("Insert failed for item $x: " . $stmt->error); } } $stmt->close(); $DBconnection->close(); ?>
额外建议
- 可以给
url字段添加唯一约束,避免插入重复的文章条目; - 考虑使用批量插入(如
INSERT INTO ... VALUES (...), (...), ...)来提高插入效率,减少数据库交互次数; - 增加脚本超时时间(如
set_time_limit(300);),避免处理大量Feed时超时。
内容的提问来源于stack exchange,提问作者Damage
相关产品推荐
相关产品推荐

