在我的 symfony2 命令中,我正在运行一个将数十万个 url(作为字符串)插入到文档中的脚本。
这是我正在使用的 2 个文档的基本结构。在程序运行之前,mongodb中已经有数千个ParentDocuments,但ChildDocuments为零:
ParentDocument:
$id:id
$subDocument:OneToManyReference(ChildDocument)
$etc:everythingelse
ChildDocument:
$id:id
$url:string
$parentDocument:ManyToOneReference(ParentDocument)
还有我的命令代码:
$dm = $this->getContainer()->get('doctrine_mongodb.odm.document_manager');
$parentDocuments = $dm->repository('My:Bundle:ParentDocument')->findAll();
while ($parentDocument = $parentDocuments->getNext()) {
//Returns an array of hundreds of thousands urls
$urls = $this->somehowFetchUrlsRelatedToTheParentDocument($parentDocument);
foreach ($urls as $url) {
$subDocument = new SubDocument();
$subDocument->setUrl($url);
$subDocument->setParentDocument($parentDocument);
$dm->persist($subDocument);
}
$dm->flush();
}
当我运行这个简单的命令时,一开始的写入速度非常快。但是,在插入数百万行的情况下,写入速度会明显变慢。命令运行 10 分钟后,速度慢到每秒 1 次写入,使代码极其无效。
我解决此问题的第一次尝试是在文档管理器刷新后立即清除它,$dm->clear();
但这意味着文档管理器将失去对当前 ParentDocument 的跟踪。所以我的解决方案是这样的:
$dm = $this->getContainer()->get('doctrine_mongodb.odm.document_manager');
$parentDocumentCursors = $dm->repository('My:Bundle:ParentDocument')->findAll();
$parentDocuments = array();
while ($parentDocument = $parentDocumentCursors->getNext()) {
array_push($parentDocuments, $parentDocument);
}
$dm->clear();
unset($dm);
$dm = $this->getContainer()->get('doctrine_mongodb.odm.document_manager');
foreach ($parentDocuments as $parentDocument) {
$urls = $this->somehowFetchUrlsRelatedToTheParentDocument($parentDocument);
foreach ($urls as $url) {
$subDocument = new SubDocument();
$subDocument->setUrl($url);
$subDocument->setParentDocument($parentDocument);
$dm->persist($subDocument);
}
$dm->flush();
$dm->clear();
}
这解决了问题。在整个程序执行过程中,写入速度始终保持快速,并且能够在没有逐渐延迟的情况下插入数百万行。
但是,这感觉像是一种不好的做法和快速修复黑客。使用文档管理器在 Symfony2 中插入数百万行而不会使读/写速度变慢的最佳实践是什么?