PHP脚本执行至第4次sleep后中断,批量用户导入异常
我碰到过好几次类似的批量处理脚本中断的问题,结合你的代码和描述,从几个核心方向帮你分析解决:
1. 优先切换到CLI模式运行脚本
这大概率是导致脚本中途中断的核心原因!如果你是通过网页浏览器访问这个方法来执行导入,web服务器(Apache/Nginx)都会有全局的请求超时限制(比如Apache的Timeout默认是60秒,Nginx的proxy_read_timeout默认60秒),不管PHP的max_execution_time设置多少,服务器都会主动断开长时间运行的请求。
解决方法:
- 用命令行直接运行脚本(CLI模式),PHP在CLI下默认
max_execution_time是0(无限制),也不会受web服务器超时影响。 - 如果必须通过web触发,先在脚本开头添加:
set_time_limit(0); // 取消PHP执行时间限制 ini_set('memory_limit', '256M'); // 根据需要调整内存限制,比如512M
同时还要修改web服务器的超时配置(比如Apache改Timeout 3600,Nginx改proxy_read_timeout 3600;),但CLI模式是最省心的方案。
2. 排查CSV文件读取的稳定性问题
你提到即使注释数据库和邮件逻辑,只遍历打印也会莫名停顿,这很可能是fgetcsv遇到了格式异常的行(比如字段里包含未转义的换行符、分隔符,或者编码问题),导致fgetcsv卡住无法继续读取下一行。
替换成更稳健的CSV读取方式:
// 替换原有的fopen和while循环 $filename = FCPATH . 'assets/overdracht_users.csv'; // 用FCPATH代替base_url,避免URL访问文件的问题 $file = new SplFileObject($filename); $file->setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE); $file->setCsvControl(';'); $count = 0; foreach ($file as $mappedData) { $count++; // 跳过表头行 if ($count === 1) continue; // 检查当前行是否有效 if (empty($mappedData[0])) { error_log("Empty email at row {$count}"); array_push($importFails, $mappedData); continue; } // 后续的用户处理逻辑... }
注意:用FCPATH代替base_url()来读取本地文件,base_url()返回的是HTTP URL,用fopen访问可能会有额外的网络开销或权限问题,直接用服务器本地路径更可靠。
3. 调整休眠逻辑的触发条件
你当前用$count %50 ==0来触发休眠,但$count包含了表头行和未处理的空行,导致休眠时机和实际处理的用户数不匹配。建议用实际成功导入的用户数$totalImported来判断:
// 替换原有的休眠判断 if ($totalImported > 0 && $totalImported % 50 === 0) { echo "Processed {$totalImported} users, sleeping for " . $randSleep . " seconds...\n"; $randSleep = rand(20, 40); sleep($randSleep); echo "Resuming processing...\n"; }
这样每次处理完50个有效用户才休眠,逻辑更准确。
4. 避免内存泄漏与日志优化
3500条数据虽然不多,但如果$importFails和$mailFails数组持续增大,加上循环中创建的对象未及时回收,可能会触发内存限制导致脚本中断。
优化方案:
- 定期将错误日志写入文件,清空数组:
// 处理完每100条失败记录后写入文件并清空 if (count($importFails) >= 100) { file_put_contents('import_failures.log', print_r($importFails, true), FILE_APPEND); $importFails = []; }
- 用
error_log()代替var_dump()记录运行状态,避免大量输出导致缓冲区阻塞:
// 替换var_dump("count is " . $count); error_log("Processed row count: {$count}, imported users: {$totalImported}");
5. 添加错误捕获机制
在用户处理逻辑外层包裹try-catch,捕获异常并记录详细信息,方便定位具体哪一行出问题:
try { // 从创建$user对象到数据库插入、邮件发送的所有逻辑 } catch (Exception $e) { error_log("Error processing row {$count}: " . $e->getMessage() . "\nStack trace: " . $e->getTraceAsString()); array_push($importFails, $mappedData); continue; }
按照以上步骤调整后,脚本的稳定性应该会大幅提升,先从CLI模式运行开始测试,这是解决中断问题最关键的一步。
内容的提问来源于stack exchange,提问作者Dennis

