学说事务使用过多内存问题


Doctrine transaction using too much memory issue

我在运行我的一个脚本时不断收到此错误;

PHP 致命错误:允许的内存大小 已耗尽的 1073741824 字节数(已尝试 分配 71 字节)在 ... lib/symfony-1.4.11/lib/plugins/sfDoctrinePlugin/lib/vendor/doctrine/Doctrine/Connection/Statement.php 在第 246 行,...

以下是脚本的精简版本触发错误;

public function executeImportFile(sfWebRequest $request)
{
 ini_set('memory_limit', '1024M');
 set_time_limit ( 0 );
 //more codes here...
 $files = scandir($workspace.'/'.$directory);
 foreach ($files as $file) {
   $path = $workspace.'/'.$directory.'/'.$file;
   if ($file != "." && $file != "..") {
     $this->importfile($path);
   }
 }
}

protected function importfile($path){

 $connection =
sfContext::getInstance()->getDatabaseManager()->getDatabase('doctrine')->getDoctrineConnection();
 $connection->beginTransaction();
 try {
   //more codes here...

   while ($data = $reader->read()) //reads each line of a csv file
   {
     // send the line to another private function to be processed
     // and then write to database
     $this->writewave($data);
   }

   $connection->commit();
 } catch (Exception $e) {
   $connection->rollback();
 }
}

该脚本所做的基本上是读取文件夹(每个包含数万行),处理它,并使用 Doctrine 的事务将其写入数据库。

虽然我认为我不需要设置内存限制和时间限制在这两个函数中,脚本退出,因为 Doctrine 用完了所有分配了 1GB 内存。

它通常会在处理 10 个文件并分配更多文件后停止内存将允许它处理更多的文件,并且仍然崩溃。

我在这里缺少什么导致内存不处理完每个文件后释放?

莫哈末·沙基尔·扎卡里亚http://www.mohdshakir.net

尽可能释放任何对象,包括查询/连接对象,尤其是当它们位于循环内时。

http://docs.doctrine-project.org/projects/doctrine1/en/latest/en/manual/improving-performance.html#free-objects

Doctrine 在

处理大型数据集时会使用大量内存 - 以这种方式导入大型/多个文件是不可能的。

即使您在单独的函数调用中导入每个文件,doctrine 也有一个内部对象缓存,因此它们不会被释放。

最好的选择是稍微修改任务,使其接受文件名作为参数。如果已通过,则仅处理该文件(希望它不会变得太大)。如果没有传递文件名,它会像现在一样遍历所有文件,并通过 exec 调用自己,所以这是一个不同的过程,内存真的被释放了。

我在你的脚本中看到的最大问题是你经常调用sfcontext。恕我直言,sfcontext 不是单例,这意味着您每个循环都会创建一个新实例。您不能将连接传递到方法中吗?

public function executeImportFile(sfWebRequest $request)
{
 ini_set('memory_limit', '1024M');
 set_time_limit ( 0 );

$connection = sfContext::getInstance()->getDatabaseManager()->getDatabase('doctrine')->getDoctrineConnection();
 //more codes here...
 $files = scandir($workspace.'/'.$directory);
 foreach ($files as $file) {
   $path = $workspace.'/'.$directory.'/'.$file;
   if ($file != "." && $file != "..") {
     $this->importfile($path, $connection);
   }
 }
}

protected function importfile($path, $connection){
 $connection->beginTransaction();
 try {
   while ($data = $reader->read()) //reads each line of a csv file
   {
     $this->writewave($data);
   }
  $connection->commit();
 } catch (Exception $e) {
   $connection->rollback();
 }
}