6

私は100 000行以上のテーブルを持っており、ドクトリンですべてを選択し、各行でいくつかのアクションを実行したい.ドクトリンを使用したsymfony2で、次のクエリを実行しようとしています:

    $query = $this->getDefaultEntityManager()
        ->getRepository('AppBundle:Contractor')
        ->createQueryBuilder('c')
        ->getQuery()->iterate();

    foreach ($query as $contractor) {
        // doing something
    }

しかし、すべてのデータをメモリに書き込んだと思うので、メモリリークが発生します。

私はそうするとき、そのライブラリでADOdbでより多くの経験を持っています:

$result = $ADOdbObject->Execute('SELECT * FROM contractors');
   while ($arrRow = $result->fetchRow()) {
        // do some action
   }

メモリリークは発生しません。

では、テーブルからすべてのデータを選択し、symfony2 の doctrine でメモリ リークが発生しないようにするにはどうすればよいでしょうか?

質問編集

foreach を削除して繰り返し実行しようとすると、メモリ リークも発生します。

$query = $this->getDefaultEntityManager()
            ->getRepository('AppBundle:Contractor')
            ->createQueryBuilder('c')
            ->getQuery()->iterate();
4

3 に答える 3

6

通常のアプローチはiterate()を使用することです。

$q = $this->getDefaultEntityManager()->createQuery('select u from AppBundle:Contractor c');
$iterableResult = $q->iterate();
foreach ($iterableResult as $row) {
    // do something
}

ただし、教義のドキュメントに記載されているように、これでもエラーが発生する可能性があります。

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

これに対する最も簡単なアプローチは、オフセットと制限を使用してより小さなクエリを作成することです。

//get the count of the whole query first
$qb = $this->getDefaultEntityManager();
$qb->select('COUNT(u)')->from('AppBundle:Contractor', 'c');
$count = $qb->getQuery()->getSingleScalarResult();

//lets say we go in steps of 1000 to have no memory leak
$limit = 1000;
$offset = 0;

//loop every 1000 > create a query > loop the result > repeat
while ($offset < $count){
    $qb->select('u')
        ->from('AppBundle:Contractor', 'c')
        ->setMaxResults($limit)
        ->setFirstResult($offset);
    $result = $qb->getQuery()->getResult();
    foreach ($result as $contractor) {
        // do something
    }
    $offset += $limit;
}

この大量のデータセットでは、最大実行時間(デフォルトでは30 秒) を超える可能性が高くなります。そのため、php.ini のset_time_limitを手動で変更してください。既知のパターンですべてのデータセットを更新するだけの場合は、PHP で結果をループして編集するのではなく、1 つの大きな更新クエリを作成することを検討する必要があります。

于 2015-10-21T08:37:19.100 に答える
0

このアプローチを使用してみてください:

foreach ($query as $contractor) {
    // doing something

    $this->getDefaultEntityManager()->detach($contractor);
    $this->getDefaultEntityManager()->clear($contractor);
    unset($contractor); // tell to the gc the object is not in use anymore

}

この助けを願っています

于 2015-10-21T07:24:45.610 に答える