2016-04-25 83 views
1

我有几个数据库管理任务需要经过数据库中的每条记录。这是我的理解是与CakePHP的ORM 3.x的,我可以做这样的事情,它会永远只能在内存中有一条记录时间:每次读取记录时内存使用量都会增加

$records = TableRegistry::get('Whatever')->find(); 
foreach ($records as $record) { 
    // do some processing 
} 

然而,这最终有崩溃“内存不足“的例外。我已经添加了一些memory_get_peak_usage的日志记录,并且每次迭代都会增加,即使除了foreach循环内发生的日志记录之外没有其他任何内容。循环中每次增加约12K。

我正在运行3.2.7,并且结果类似,无论我是否启用了调试和/或SQL日志记录。将频繁拨打的电话添加到gc_collect_cycles()只会降低过程速度,这对内存使用量没有帮助。

这是预计,或者一个错误?如果前者,有什么我可以以不同的方式在这个代码,以防止它? (很显然,我可以小批量处理它,但这不是一个很好的解决方案。)

+0

您是否尝试过关闭[**结果缓冲**](http://book.cakephp.org/3.0/en/orm/retrieving-data -and-resultsets.html#工薪与对结果集)? – ndm

+0

@ndm,听起来很有希望,所以我跑了几个快速测试,但有些奇怪的是似乎显示内存使用增加*更快*缓冲关闭。完全可能的是,我在测试中做了错误的测试......其他测试崩溃了,告诉我“在其他未缓冲的查询处于活动状态时无法执行查询”,这对我来说可能是一种破坏行为,在这种情况下。我将不得不考虑这个问题,看看是否有解决方案,对代码的干扰性小于运行我需要小批量处理的大查询。 –

回答

0

从我的理解,它是预期的行为,因为当您开始迭代对象时执行带有ORM的查询生成($记录)。因此所有的数据都被加载到内存中,然后你逐个遍历每个条目。

如果你想限制内存使用量,我建议你看看limitoffset。有了这些,您可以提取要使用的子集,从而限制内存使用量。

+0

我以为新的ORM只能以这种方式一次检索一个记录。如果我在结果集上调用'toArray',那么肯定它会一次加载所有内容,但事实是它随着它的增加而增加似乎表明它没有。 –

+0

我觉得你很困惑[懒惰的评价](http://book.cakephp.org/3。0/EN/ORM /查询builder.html#如何,都查询 - 惰性计算)。我不是ORM的专家,但我非常确定要学习[查询构建器](http://book.cakephp.org/3.0/en/orm/query-builder.html)和[collections](http ://book.cakephp.org/3.0/en/core-libraries/collections.html)将帮助你优化你的查询:)另外,我刚刚看了[Advanved querying](https://www.youtube.com/watch? v = rBRy5BiCeew)这种类型的演示ORM的力量。可能对你也有用:) –

+0

如果它一次将所有记录检索到内存中,然后一次一个地检查它们,它会立即崩溃。 (或者,如果我读了一个较小的数字,内存将立即跳转到其高峰使用状态,然后只是坐在那里相对不变)。每次迭代内存使用量稳定增加直到崩溃告诉我这是事实一次只能提取一条记录,但在进入下一条记录时不会处理该对象。这对我来说很清楚;也许我没有解释得很好。 –

0

CakePHP 3.x ORM内置了用于ResultSet对象的查询缓存。当您对结果集进行迭代时,实体将存储在内部数组中。这样做是为了让你可以倒回迭代器并重新循环。

如果您打算只迭代一次较大的结果集,并且想要减少内存使用量,则必须禁用结果缓冲。

$records = TableRegistry::get('Whatever')->find()->bufferResults(false); 
foreach ($records as $record) { 
    // do some processing 
} 

缓冲关闭时,实体从结果集中取出,之后应该没有对它的引用。

此功能的文档不在CakePHP书中,但它应该是。

这里的API参考:

https://api.cakephp.org/3.3/class-Cake.Database.Query.html#_bufferResults