Php 使用Doctrine将大量数据插入MongoDB集合

Php 使用Doctrine将大量数据插入MongoDB集合,php,mongodb,doctrine-orm,Php,Mongodb,Doctrine Orm,我将Doctrine和MongoDB用于一个应用程序,有一个任务应该将数据从CSV文件导入到集合中。大约有5个不同的CSV文件,每个文件至少有450.000个条目,这些条目每年应该重要1到3次 目前,我遍历文件的所有行,创建一个对象,调用persist()并每隔2.500个项目刷新一次 每个项目都不是很大,它有一个ID、一个10-20个字符的字符串、一个5-10个字符的字符串和一个布尔值 我的第一个问题是: 当我每5.000个项目刷新一次时,插入速度明显变慢。在我的测试环境中,刷新5000个项目

我将Doctrine和MongoDB用于一个应用程序,有一个任务应该将数据从CSV文件导入到集合中。大约有5个不同的CSV文件,每个文件至少有450.000个条目,这些条目每年应该重要1到3次

目前,我遍历文件的所有行,创建一个对象,调用
persist()
并每隔2.500个项目刷新一次

每个项目都不是很大,它有一个ID、一个10-20个字符的字符串、一个5-10个字符的字符串和一个布尔值

我的第一个问题是: 当我每5.000个项目刷新一次时,插入速度明显变慢。在我的测试环境中,刷新5000个项目大约需要102秒,刷新2500个项目大约需要10秒

一段时间后,冲洗速度变慢。如前所述,开始冲洗2.500件物品大约需要10秒,在冲洗100.000件物品之后,冲洗2.500件物品几乎需要1分钟


我是否可以进行任何优化以加快速度?

此导入脚本是否需要使用Doctrine ODM?对于整个应用程序-当然。但是,用如此多的开销处理大量数据并不是最优的,因为PHP在保存和处理大型集合方面效率不高


您可以做的最佳优化是跳过该层,直接使用mongodb类,并确保逐行读取文件,而不是一次读取所有文件。如果您对MongoDB有一定的网络延迟,那么使用batchInsert()也会大大加快您的速度(但您永远不需要将其推送到5k文档)。

此导入脚本是否有任何理由需要使用ODM?对于整个应用程序-当然。但是,用如此多的开销处理大量数据并不是最优的,因为PHP在保存和处理大型集合方面效率不高


您可以做的最佳优化是跳过该层,直接使用mongodb类,并确保逐行读取文件,而不是一次读取所有文件。如果您对MongoDB有一些网络延迟,那么使用batchInsert()也会大大加快速度(但您永远不需要将其推送到5k文档)。

我认为脚本中有两个部分可以优化

  • 读取CSV文件的方式取决于加载方式,可以将其完全加载到内存中(例如使用
    file\u get\u contents()
    file()
    ),也可以使用
    fopen()
    fread()
    逐个读取
首选最后一个选项,因为它在处理一堆行时只需要占用必要的内存量

  • 您需要
    清除()
    已处理的对象,否则它将保留在内存中,直到脚本结束。这意味着,如果一个DC2对象使用1Mo内存,而您有100000个对象,那么在脚本结束时,它将使用100000个内存。 因此,按2500范围批处理insert是一个不错的主意,但显然需要从EntityManager中删除已处理的对象
可以使用
$entityManager->clear()完成

clear()
将清除整个
EntityManager
,如果要清除单个实体,可以使用
$EntityManager->detach($object)


如果您想分析内存使用情况,您可能还对函数感兴趣


我认为脚本中有两个部分可以优化

  • 读取CSV文件的方式取决于加载方式,可以将其完全加载到内存中(例如使用
    file\u get\u contents()
    file()
    ),也可以使用
    fopen()
    fread()
    逐个读取
首选最后一个选项,因为它在处理一堆行时只需要占用必要的内存量

  • 您需要
    清除()
    已处理的对象,否则它将保留在内存中,直到脚本结束。这意味着,如果一个DC2对象使用1Mo内存,而您有100000个对象,那么在脚本结束时,它将使用100000个内存。 因此,按2500范围批处理insert是一个不错的主意,但显然需要从EntityManager中删除已处理的对象
可以使用
$entityManager->clear()完成

clear()
将清除整个
EntityManager
,如果要清除单个实体,可以使用
$EntityManager->detach($object)


如果您想分析内存使用情况,您可能还对函数感兴趣


您如何读取CSV文件?在flush()操作后是否清除EntityManager?如果不是,那就意味着每个对象都存储在内存中,使事情变得更慢。@BorisGuéry谢谢,这帮了大忙。如果您将此添加为答案,我将接受。您如何读取CSV文件?在flush()操作后是否清除EntityManager?如果不是,那就意味着每个对象都存储在内存中,使事情变得更慢。@BorisGuéry谢谢,这帮了大忙。如果你加上这个作为回答,我会接受的。这只是为了方便使用。当我像@Boris Guéry建议的那样使用
clear()
时,它工作得相当好。然而,如果我不能将它缩短到一个可接受的时间,我会切换到直接调用MongoDB。好吧,在正常的应用程序情况下,处理不超过12个文档是很方便的。然而,它确实(和他们的ORM一样)有巨大的开销,并且有更多的查询——更多的开销。似乎它已经使您在批处理脚本中的情况变得不那么方便了,因为它会使您为解决它所导致的问题而奔波。技巧是将Doctrine ODM插入剥离到裸露的底部(将int插入到新集合中),然后使用本机驱动程序对batchInsert执行相同的操作,这样您将看到