Php 大容量插入会导致内存使用失控
我试图使用Doctrine2和Symfony2 fixture bundle在MySQL数据库中插入大量数据(30000多行) 我看了看。我看到了很多关于内存泄漏和教义的问题,但没有给我满意的答案。它经常出现在函数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
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李>
$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个对象,如何识别未插入的对象以及如何获得插入对象的确切数量?确认了此解决方案。带来了一个从几个小时减少到几分钟的导入。只有--没有调试为我完成了!