Php 大容量插入会导致内存使用失控

Php 大容量插入会导致内存使用失控,php,symfony,memory,doctrine-orm,Php,Symfony,Memory,Doctrine Orm,我试图使用Doctrine2和Symfony2 fixture bundle在MySQL数据库中插入大量数据(30000多行) 我看了看。我看到了很多关于内存泄漏和教义的问题,但没有给我满意的答案。它经常出现在函数clear()中 所以,我做了各种形状的这个: while (($data = getData()) { $iteration++; $obj = new EntityObject(); $obj->setName('henry'); // Fi

我试图使用Doctrine2和Symfony2 fixture bundle在MySQL数据库中插入大量数据(30000多行)

我看了看。我看到了很多关于内存泄漏和教义的问题,但没有给我满意的答案。它经常出现在函数
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
编辑(部分解决方案)

@Qoopmao带来了一个解决方案,可以显著减少内存消耗,禁用doctrine sql logger:
$manager->getConnection()->getConfiguration()->setSQLLogger(null)


然而,它仍然异常高且不断增加。

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

MySQL有一个很棒的东西叫做。我宁愿使用它,甚至我必须先清理我的csv,然后再加载

如果您需要更改值,我会将csv读取到array
$csvData=array\u map(“str\u getcsv”,file($csv))。更改阵列上需要的任何内容并将其保存到行中。之后,使用新的.csv加载MySQL


为了支持我的主张,为什么我不使用顶部描述的原则。

我使用@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
参数也非常重要。然后内存消耗是稳定的。

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

use Doctrine\ORM\EntityManagerInterface;


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

您是否尝试降低批量大小(500)?速度会较慢,但内存强度会降低是的。我试着降低它(100,20,1)并增加它(1000,2000,5000),没有变化。只是好奇这里,getData()返回什么?它从哪里获得信息?多少钱?与文档相比,循环的选择很有趣,我想你是在dev中这样做的吗?如果是这样,您最好禁用SQL记录器(
$manager->getConnection()->getConfiguration()->setSQLLogger(null)
)。@Gui Don,您能试试这个吗?陛下这是一个遗憾,因为据我所知,有些人说它不是批量插入的最佳工具,主要是从性能的角度来看。然而,在我的例子中,我并不真正关心性能(它是数千行,而不是数百万行!)。它应该能正常工作。话虽如此,谢谢你的帮助,我会试试的。但是,如何方便地建立与负载数据的关系?我是否必须为每个已建立关系的表创建一个CSV?是的,我理解性能对您来说可能不是很重要,但如果您能以最佳性能完成此任务,我认为这样做会很酷。您不能对多个表执行此操作,但可以创建一个试探表,加载到试探表中,然后插入到表中,从试探表中选择。这听起来很棘手,但实际上这样做更好。您可以使用dbal连接对象(甚至pdo连接对象)来准备和执行sql insert语句,而不是直接访问mysql loadddata。这也将允许您插入关系。我使用这种方法处理5K左右相当复杂的实体。30K可能还是一段时间。php本身并不是为大批量作业而设计的。保存批处理时,从entityManager分离对象,但不清除$tempObjects。这意味着,如果保存2000个项目,将分离500个对象,然后分离1000个对象,然后分离1500个对象。保存批处理时清除$tempObjects没有意义。@JeremyQuinton你说得对,他在分离$tempObjects后需要重置它们。如果我们想插入50000个对象,但执行此代码后只插入了48000个对象,如何识别未插入的对象以及如何获得插入对象的确切数量?确认了此解决方案。带来了一个从几个小时减少到几分钟的导入。只有--没有调试为我完成了!