【问题标题】:PHP PDO fetch() loop dies after processing part of large dataset处理部分大型数据集后,PHP PDO fetch() 循环终止
【发布时间】:2015-01-12 08:38:41
【问题描述】:

我有一个 PHP 脚本,它在一个典型的循环中将来自 PDO 查询的“大型”数据集(大约 10 万条记录)处理成单个对象集合:

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 记录块。没有不同。循环在同一个地方断裂。但是,通过在每批结束时“清理” PDO 语句对象,我能够将处理的总数提高到 54%。

其他奇怪的行为:

  • 当我使用 ini_set('memory_limit', '1024MB') 设置内存限制时,循环实际上比使用更小的内存限制提前终止,进度大约为 20%。
  • 在此循环期间,PHP 进程使用 100% 的 CPU,但一旦中断,使用率会下降到 2%,尽管随后立即在另一个循环中进行处理。很可能,在第一个循环中与 MySQL 服务器的连接非常耗费资源。

如果这有什么不同的话,我会在本地使用 MAMP PRO 进行所有这些操作。

还有什么其他东西可以持续打破这个循环,我没有检查过吗?这难道不是处理这么多记录的可行策略吗?

更新

在使用批处理策略(20K 增量)后,我开始在第三批前后一直看到 MySQL 错误:MySQL server has gone away;可能是长时间运行的无缓冲查询的症状。

【问题讨论】:

  • 如果您在浏览器上运行脚本,它会返回 500 内部服务器错误或白色空白页吗?您是否还在 php.ini 上打开了错误和警告?最近,当我尝试通过 php 将一个巨大的数据集从 xml 导入 mysql 时,发生了类似的事情。
  • @ADASein,正如我们所说的那样,我正在尝试一种批处理方法,但没有取得多大成功,将处理分成大约 20K 条记录的块。如果我确认这也不起作用,我会更新我的问题。
  • @HddnTHA,不,这不是内部服务器错误,$query->fetch() 未能在大约 50K 迭代点返回记录,因此 while 循环中断。
  • 首先确保您启用了错误报告并且仍然没有记录错误(看起来就是这种情况)。如果发生这种情况,那么 PHP 进程会出现段错误,这是 a) 错误的指示,b) 不是您可以直接修复的问题。大多数情况下,这是由于 PHP 扩展中的一些错误造成的,但有时它也是核心。通常有解决方法。如果沿着这条路走下去,我建议在 Thing 构造函数中仔细查看任何不是沼泽标准 PHP 的东西。
  • 使用 try..catch 块没有捕获任何异常?

标签: php mysql loops memory pdo


【解决方案1】:

如果您真的需要即时处理 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

【讨论】:

    猜你喜欢
    • 2019-11-01
    • 2011-02-15
    • 2013-01-11
    • 2013-09-05
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多