使用Doctrine批量插入

时间:2015-10-30 00:37:43

标签: php symfony memory doctrine-orm

我正在尝试使用Doctrine2和Symfony2 fixture包在MySQL数据库中插入大量数据(3万多行)。

我看了the right way to do it。我看到很多关于内存泄漏和Doctrine的问题,但对我来说没有令人满意的答案。通常是Doctrine clear()函数。

所以,我做了各种各样的形状:

while (($data = getData()) {
    $iteration++;

    $obj = new EntityObject();
    $obj->setName('henry');
    // Fill object...

    $manager->persist($obj);

    if ($iteration % 500 == 0) {
        $manager->flush();
        $manager->clear();

        // Also tried some sort of:
        // $manager->clear($obj);   
        // $manager->detach($obj);
        // gc_collect_cycles();
    }
}

flush()之后,PHP内存仍然很疯狂(我很确定)。事实上,每次刷新实体时,内存都会根据批量大小和实体上升一定量,直到达到致命允许的内存大小耗尽错误。使用非常小的实体,它可以工作,但内存消耗增加太多:几MB而应该是KB。

clear()detach()或调用GC似乎根本没有效果。它只清除一些KB。

我的方法有缺陷吗?我在某处错过了什么吗?这是一个错误吗?

更多信息

  • 没有flush()内存几乎不动;
  • 降低批次不会改变结果;
  • 数据来自需要消毒的CSV;

编辑(部分解决方案)

@qooplmao带来了一个显着降低内存消耗的解决方案,禁用了doctrine sql logger:$manager->getConnection()->getConfiguration()->setSQLLogger(null);

然而,它仍然异常高并且在增加。

3 个答案:

答案 0 :(得分:9)

我使用this resource解决了我的问题,正如@Axalix建议的那样。

这是我修改代码的方式:

// IMPORTANT - Disable the Doctrine SQL Logger
$manager->getConnection()->getConfiguration()->setSQLLogger(null);

// SUGGESION - make getData as a generator (using yield) to to save more memory.
while ($data = getData()) {
  $iteration++;

  $obj = new EntityObject();
  $obj->setName('henry');
  // Fill object...

  $manager->persist($obj);

  // IMPORTANT - Temporary store entities (of course, must be defined first outside of the loop)
  $tempObjets[] = $obj;

  if ($iteration % 500 == 0) {
    $manager->flush();

    // IMPORTANT - clean entities
    foreach($tempObjets as $tempObject) {
      $manager->detach($tempObject);
    }

    $tempObjets = null;
    gc_enable();
    gc_collect_cycles();
  }
}

// Do not forget the last flush
$manager->flush();

最后但并非最不重要的是,当我将此脚本与Symfony数据夹具一起使用时,在命令中添加--no-debug参数也非常重要。然后内存消耗稳定。

答案 1 :(得分:0)

我发现 Doctrine 在执行期间记录所有 SQL。我建议用下面的代码禁用它,它确实可以节省内存:

use Doctrine\ORM\EntityManagerInterface;


public function __construct(EntityManagerInterface $entity_manager)
{
    $em_connection = $entity_manager->getConnection();
    $em_connection->getConfiguration()->setSQLLogger(null);
}

答案 2 :(得分:-2)

我的建议是放弃批量插入的Doctrine方法。我真的很喜欢Doctrine,但我只讨厌批量插入这种东西。

MySQL有一个名为LOAD DATA的好东西。我宁愿使用它,或者即使我必须首先清理我的csv并在之后执行LOAD。

如果您需要更改值,我会将csv读取到数组$csvData = array_map("str_getcsv", file($csv));。更改阵列上您需要的任何内容并将其保存到该行。之后,使用新的.csv与MySQL加载。

支持我关于为什么我不会在最高层描述的here使用Doctrine的说法。