本文详解php脚本在批量插入多列数据时中途停止(通常20–50条后)的根本原因——api请求频率超限(http 429),并提供延迟控制、错误捕获、sql安全加固及批量更新优化等实用解决方案。
在使用 PHP 批量从外部 API 获取数据并写入 MySQL 数据库时,看似逻辑正确的脚本却频繁在处理第 20–50 条记录时中断,这并非数据库连接或 SQL 语法问题,而极大概率源于上游 API 的速率限制(Rate Limiting)。正如实际调试所揭示的:调用 curl_error($curl) 后明确返回了 {“error”:”429 – Too many requests in too short timeframe…”} —— 这是典型的 HTTP 429 Too Many Requests 响应,表明目标 API(如 Statistics Norway 的 SSB API)为防止滥用,对单位时间内的请求数做了严格限制。
? 根本原因分析
- 原脚本对每个市镇(共约 360 个)发起独立的 cURL POST 请求,无任何节流机制;
- 每次请求需建立 TCP 连接、传输 JSON 查询体、等待响应、解析 JSON,耗时虽短但累积密集;
- 多数政府/公共 API(如 SSB、GeoNorge)默认限制为 ~60 次/分钟 或更严,超出即返回 429;
- 错误未被捕获,curl_exec() 返回 false,后续 $test = json_decode($resp, true) 得到 null,导致 $test[‘value’][0] 等访问触发 Notice,最终 mysqli_query() 因空值或非法 SQL 报错中断流程。
✅ 推荐解决方案(四步优化)
1. 添加健壮的 cURL 错误与 HTTP 状态检查
$resp = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$curlErr = curl_error($curl);
if ($resp === false || $httpCode !== 200) {
$errorMsg = "cURL failed for kommune $kommune ({$kommune_navn}): ";
$errorMsg .= $curlErr ?: "HTTP {$httpCode}";
error_log($errorMsg); // 记录到日志,避免阻塞页面输出
echo "⚠️ Skipped $kommune due to API error.<br>";
curl_close($curl);
continue; // 跳过当前市镇,继续下一轮
}
curl_close($curl);
2. 实施请求节流(简单有效)
在循环末尾添加 usleep() 实现毫秒级延迟,例如每请求间隔 1–2 秒:
// 在本次更新完成后、下一次循环开始前插入 usleep(1500000); // 1.5 秒,确保远低于 60req/min 限制
? 提示:usleep(1500000) ≈ 1.5 秒,360 条 × 1.5s ≈ 9 分钟完成,安全且可接受。可根据实际限流策略微调(如 usleep(1000000) 对应 60req/min)。
3. 修复 SQL 安全隐患与变量错误
原代码存在两处高危问题:
- ❌ 使用未转义的变量直接拼接 SQL(易受 SQL 注入);
- ❌ mysqli_error($conn) 中 $conn 未定义(应为 $tilkobling);
✅ 正确做法:使用预处理语句(Prepared Statements):
立即学习“PHP免费学习笔记(深入)”;