13. Batch Processing

This chapter shows you how to accomplish bulk inserts, updates and deletes with Doctrine in an efficient way. The main problem with bulk operations is usually not to run out of memory and this is especially what the strategies presented here provide help with.

この章では、効率的な方法で Doctrine を使用して大量の挿入、更新、および削除を行う方法を示します。一括操作の主な問題は、通常、メモリ不足にならないようにすることです。これは特に、ここで説明する戦略が役立つものです。

Warning

警告

An ORM tool is not primarily well-suited for mass inserts, updates or deletions. Every RDBMS has its own, most effective way of dealing with such operations and if the options outlined below are not sufficient for your purposes we recommend you use the tools for your particular RDBMS for these bulk operations.

ORM ツールは、主に一括挿入、更新、または削除には適していません。すべての RDBMS には、そのような操作を処理するための独自の最も効果的な方法があります。以下に概説するオプションが目的に十分でない場合は、これらの一括操作に特定の RDBMS 用のツールを使用することをお勧めします。

Note

ノート

Having an SQL logger enabled when processing batches can have a serious impact on performance and resource usage. To avoid that you should disable it in the DBAL configuration:

バッチを処理するときに SQL ロガーを有効にすると、パフォーマンスとリソースの使用に深刻な影響を与える可能性があります。これを避けるには、DBAL 構成で無効にする必要があります。

<?php
$em->getConnection()->getConfiguration()->setSQLLogger(null);

13.1. Bulk Inserts

Bulk inserts in Doctrine are best performed in batches, taking advantage of the transactional write-behind behavior of an EntityManager. The following code shows an example for inserting 10000 objects with a batch size of 20. You may need to experiment with the batch size to find the size that works best for you. Larger batch sizes mean more prepared statement reuse internally but also mean more work during flush.

Doctrine での一括挿入は、EntityManager のトランザクション後書き動作を利用して、バッチで実行するのが最適です。次のコードは、バッチ サイズ 20 で 10000 個のオブジェクトを挿入する例を示しています。最適なサイズを見つけるには、バッチ サイズを試してみる必要がある場合があります。バッチ サイズが大きいほど、準備済みステートメントの内部での再利用が多くなりますが、フラッシュ中の作業も多くなります。

<?php
$batchSize = 20;
for ($i = 1; $i <= 10000; ++$i) {
    $user = new CmsUser;
    $user->setStatus('user');
    $user->setUsername('user' . $i);
    $user->setName('Mr.Smith-' . $i);
    $em->persist($user);
    if (($i % $batchSize) === 0) {
        $em->flush();
        $em->clear(); // Detaches all objects from Doctrine!
    }
}
$em->flush(); // Persist objects that did not make up an entire batch
$em->clear();

13.2. Bulk Updates

There are 2 possibilities for bulk updates with Doctrine.

Doctrine での一括更新には 2 つの可能性があります。

13.2.1. DQL UPDATE

The by far most efficient way for bulk updates is to use a DQL UPDATE query. Example:

一括更新の最も効率的な方法は、DQLUPDATE クエリを使用することです。例:

<?php
$q = $em->createQuery('update MyProject\Model\Manager m set m.salary = m.salary * 0.9');
$numUpdated = $q->execute();

13.2.2. Iterating results

An alternative solution for bulk updates is to use the Query#toIterable() facility to iterate over the query results step by step instead of loading the whole result into memory at once. The following example shows how to do this, combining the iteration with the batching strategy that was already used for bulk inserts:

一括更新の代替ソリューションは、結果全体を一度にメモリにロードする代わりに、Query#toIterable() 機能を使用してクエリ結果を段階的に反復することです。はすでに一括挿入に使用されていました:

<?php
$batchSize = 20;
$i = 1;
$q = $em->createQuery('select u from MyProject\Model\User u');
foreach ($q->toIterable() as $user) {
    $user->increaseCredit();
    $user->calculateNewBonuses();
    ++$i;
    if (($i % $batchSize) === 0) {
        $em->flush(); // Executes all updates.
        $em->clear(); // Detaches all objects from Doctrine!
    }
}
$em->flush();

Note

ノート

Iterating results is not possible with queries that fetch-join a collection-valued association. The nature of such SQL result sets is not suitable for incremental hydration.

コレクション値の関連付けを取得して結合するクエリでは、結果を反復することはできません。このような SQLresult セットの性質は、インクリメンタル ハイドレーションには適していません。

Note

ノート

Results may be fully buffered by the database client/ connection allocating additional memory not visible to the PHP process. For large sets this may easily kill the process for no apparent reason.

結果は、PHP プロセスから見えない追加のメモリを割り当てるデータベース クライアント/接続によって完全にバッファリングされる場合があります。大規模なセットの場合、これにより明白な理由もなくプロセスが簡単に強制終了される可能性があります。

13.3. Bulk Deletes

There are two possibilities for bulk deletes with Doctrine. You can either issue a single DQL DELETE query or you can iterate over results removing them one at a time.

Doctrine での一括削除には 2 つの可能性があります。単一の DQL DELETE クエリを発行するか、過剰な結果を一度に 1 つずつ削除することを繰り返すことができます。

13.3.1. DQL DELETE

The by far most efficient way for bulk deletes is to use a DQL DELETE query.

一括削除の最も効率的な方法は、DQLDELETE クエリを使用することです。

Example:

例:

<?php
$q = $em->createQuery('delete from MyProject\Model\Manager m where m.salary > 100000');
$numDeleted = $q->execute();

13.3.2. Iterating results

An alternative solution for bulk deletes is to use the Query#toIterable() facility to iterate over the query results step by step instead of loading the whole result into memory at once. The following example shows how to do this:

一括削除の代替ソリューションは、結果全体を一度にメモリにロードする代わりに、Query#toIterable() 機能を使用してクエリ結果を段階的に反復することです。次の例は、これを行う方法を示しています。

<?php
$batchSize = 20;
$i = 1;
$q = $em->createQuery('select u from MyProject\Model\User u');
foreach($q->toIterable() as $row) {
    $em->remove($row);
    ++$i;
    if (($i % $batchSize) === 0) {
        $em->flush(); // Executes all deletions.
        $em->clear(); // Detaches all objects from Doctrine!
    }
}
$em->flush();

Note

ノート

Iterating results is not possible with queries that fetch-join a collection-valued association. The nature of such SQL result sets is not suitable for incremental hydration.

コレクション値の関連付けを取得して結合するクエリでは、結果を反復することはできません。このような SQLresult セットの性質は、インクリメンタル ハイドレーションには適していません。

13.4. Iterating Large Results for Data-Processing

You can use the toIterable() method just to iterate over a large result and no UPDATE or DELETE intention. $query->toIterable() returns iterable so you can process a large result without memory problems using the following approach:

toIterable() メソッドを使用して、より大きな結果を反復するだけで、UPDATE または DELETE の意図はありません。 $query->toIterable() は iterable を返すため、次のアプローチを使用して、メモリの問題なしに大きな結果を処理できます。

<?php
$q = $this->_em->createQuery('select u from MyProject\Model\User u');
foreach ($q->toIterable() as $row) {
    // do stuff with the data in the row

    // detach from Doctrine, so that it can be Garbage-Collected immediately
    $this->_em->detach($row[0]);
}

Note

ノート

Iterating results is not possible with queries that fetch-join a collection-valued association. The nature of such SQL result sets is not suitable for incremental hydration.

コレクション値の関連付けを取得して結合するクエリでは、結果を反復することはできません。このような SQLresult セットの性質は、インクリメンタル ハイドレーションには適していません。

Table Of Contents

Previous topic

12. Transactions and Concurrency

12. トランザクションと並行性

Next topic

14. Doctrine Query Language

14. Doctrine クエリ言語

This Page

Fork me on GitHub