【问题标题】:Best practice for mongodb bulk inserts in Symfony2Symfony2 中 mongodb 批量插入的最佳实践
【发布时间】:2012-11-24 08:00:43
【问题描述】:

在我的 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 中插入数百万行而不会使读/写速度变慢的最佳做法是什么?

【问题讨论】:

    标签: mongodb symfony doctrine doctrine-orm symfony-2.1


    【解决方案1】:

    我会避免使用 Symfony 的文档管理器并直接使用 batchInsert() 函数。这在 http://php.net/manual/en/mongocollection.batchinsert.php 的文档中有所描述

    【讨论】:

      【解决方案2】:

      为了在学说中进行批量插入,您需要将刷新移出循环。考虑下面的场景,您将在 foreach 中持久化,然后在 foreach 完成时刷新。您唯一的问题是,在刷新之前,您将无法查询批处理中插入的任何数据。

      $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();
      

      另一种选择是进行推送、推送或添加集。 要考虑的一个问题是您需要在 php 中使用 stdClass 才能添加对象。 我发现这是更新子文档的最快方法。 例如:

      $dm->createQueryBuilder('My:Bundle:ParentDocument')
          ->update()
          ->field('subDocument')->push( (object) array('url'=> $url) )
          ->field('id')->equals( $parentDocumentId )
          ->getQuery()
          ->execute();
      

      【讨论】:

        猜你喜欢
        • 2013-06-07
        • 2011-12-30
        • 2014-02-04
        • 1970-01-01
        • 2018-05-17
        • 2014-06-21
        • 2010-10-17
        • 2015-09-02
        • 1970-01-01
        相关资源
        最近更新 更多