我有一个 PHP 脚本,它在一个典型的循环中将来自 PDO 查询的“大型”数据集(大约 100K 条记录)处理成单个对象集合:
while ($record = $query->fetch()) {
$obj = new Thing($record);
/* do some processing */
$list[] = $obj;
$count++;
}
error_log('Processed '.$count.' records');
这个循环处理了大约 50% 的数据集,然后莫名其妙地中断了。
我尝试过的事情:
- 内存分析:
memory_get_peak_usage()
在循环终止之前始终输出大约 63MB。内存限制为 512MB,通过 php.ini 设置。 - 使用
set_time_limit()
将脚本执行时间增加到 1 小时(3600 秒)。循环在此之前很久就中断了,我在日志中没有看到这个错误的常见错误。 - 将
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY
设置为false
以避免缓冲整个数据集 - 在循环中断后立即注销
$query->errorInfo()
。这没有帮助,因为错误代码是“00000”。 - 检查 MySQL 错误日志。此脚本运行之前、之后或期间没有任何注意事项。
- 将处理分批处理成 20K 记录 block 。没有不同。环路在同一个地方断了。但是,通过在每批结束时“清理”PDO 语句对象,我能够将处理总数提高到 54%。
其他奇怪的行为:
- 当我使用
ini_set('memory_limit', '1024MB')
设置内存限制时,循环实际上比使用更小的内存限制(大约 20)更早结束% 进度。 - 在此循环中,PHP 进程使用 100% 的 CPU,但一旦中断,使用率会回落至 2%,尽管随后会立即在另一个循环中进行处理。很可能,在第一个循环中与 MySQL 服务器的连接非常耗费资源。
如果有任何不同,我将使用 MAMP PRO 在本地完成所有这些工作。
有没有其他我没有检查过的东西可以持续打破这个循环?这根本不是处理这么多记录的可行策略吗?
更新
在使用批处理策略(20K 增量)后,我开始在第三批左右看到一个 MySQL 错误:MySQL server has gone away
;可能是长时间运行的无缓冲查询的症状。
最佳答案
如果您真的需要即时处理 100K 条记录,您应该在 SQL 中进行处理,并根据需要获取结果 - 它应该可以节省很多时间。
但是由于某些原因你可能不能这样做。你总是处理语句中的所有行,所以使用 fetchAll 一次 - 然后让 MySQL 单独使用,就像那样:
$records = $query->fetchAll()
foreach ($records as record)
{
$obj = new Thing($record);
/* do some processing */
$list[] = $obj;
$count++;
}
error_log('Processed '.$count.' records');
此外,仅选择您将使用的行。 如果这没有帮助,您可以试试这个:Setting a connect timeout with PDO .
关于处理部分大型数据集后,PHP PDO fetch() 循环终止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26938728/