0

私の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秒あたり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に数百万行を挿入するためのベストプラクティスは何ですか?

4

2 に答える 2

3

Symfonyのドキュメントマネージャーの使用を避け、batchInsert()関数を直接使用します。これはhttp://php.net/manual/en/mongocollection.batchinsert.phpのドキュメントで説明されています。DoctrineのODMが実際にここであなたを傷つけているように私には感じます。

于 2012-11-24T12:06:43.767 に答える
1

教義で一括挿入を行うには、フラッシュをループの外側に移動する必要があります。以下のシナリオを考えてみましょう。このシナリオでは、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();

もう1つのオプションは、プッシュ、プッシュ、または追加セットを実行することです。考慮すべき問題の1つは、オブジェクトを追加するためにphpでstdClassを使用する必要があることです。これがサブドキュメントを更新する最も簡単な方法だと思います。例えば:

$dm->createQueryBuilder('My:Bundle:ParentDocument')
    ->update()
    ->field('subDocument')->push( (object) array('url'=> $url) )
    ->field('id')->equals( $parentDocumentId )
    ->getQuery()
    ->execute();
于 2013-06-15T11:31:26.860 に答える