web-dev-qa-db-ja.com

メモリリークSymfony2 Doctrine2 /メモリ制限を超える

実際、symfony2とdoctrine2の組み合わせには多くの問題があります。私は巨大なデータセット(約300〜200万回の書き込みと読み取り)を処理しなければならず、メモリ不足を避けるためにさらに多くの努力をしなければなりません。

私は2つの主要なポイントを考え出しました。「リーク」メモリ(実際にはリークではありませんが、多くを割り当てています)

  1. Entitymanagerエンティティストレージ(このストレージの実際の名前はわかりません)処理されたすべてのエンティティを保持しているようで、このストレージを定期的にクリアする必要があります

    $ entityManager-> clear()
  2. Doctrine QueryCache-使用されたすべてのクエリをキャッシュし、使用したいキャッシュの種類を決定できることだけが見つかりました。グローバルな無効化も有用なフラグも見つかりませんでした。無効にするための各クエリです。したがって、通常は、

     $ qb = $ repository-> createQueryBuilder($ a); 
     $ query = $ qb-> getQuery(); 
     $ query-> useQueryCache(false); 
     $ query-> execute(); 
    

そう..それは私が今理解したすべてです..私の質問は次のとおりです。

EntitymanagerStorageからいくつかのオブジェクトを拒否する簡単な方法はありますか?エンティティマネージャーでクエリキャッシュの使用を設定する方法はありますか? symonfony doctrine=設定にあるこのキャッシュ動作を設定できますか?

誰かが私のためにいくつかの素敵なヒントを持っている場合、非常にクールだろう..そうでなければ、これはいくつかの新人を助けるかもしれない.

チャ

44
MonocroM

少し遅れましたが、私はちょうどあなたの質問に答える Googleグループのスレッド を見つけたと思います: Doctrine Configuration Reference デフォルトでloggingは、kernel.debugの値に設定されているため、 debugをtrueに設定してAppKernelをインスタンス化した場合、SQLコマンドは各反復でメモリに保存されます。

AppKernelをfalseにインスタンス化し、loggingfalseYMLを設定するか、EntityManagerを使用する前にSQLLoggerを手動でnullに設定します

$em->getConnection()->getConfiguration()->setSQLLogger(null);
84
Sergi

-no-debugでコマンドを実行してみてください。デバッグモードでは、プロファイラーはすべてのクエリに関する情報をメモリに保持します。

18
Arnaud Le Blanc

1。ロギングおよび_app/config/config.yml_のプロファイリングをオフにします

_doctrine:
    dbal:
        driver: ...
        ...
        logging: false
        profiling: false
_

またはコード内

_$this->em->getConnection()->getConfiguration()->setSQLLogger(null);
_

2。ガベージコレクターを強制します。 CPUを積極的に使用している場合、ガベージコレクタは待機し、すぐにメモリのない状態になります。

最初に、手動のガベージコレクション管理を有効にします。コードの任意の場所でgc_enable()を実行します。次に、gc_collect_cycles()を実行して、ガベージコレクターを強制します。

_public function execute(InputInterface $input, OutputInterface $output)
{
    gc_enable();

    // I'm initing $this->em in __construct using DependencyInjection
    $customers = $this->em->getRepository('AppBundle:Customer')->findAll();

    $counter = 0;
    foreach ($customers as $customer) {
        // process customer - some logic here, $this->em->persist and so on

        if (++$counter % 100 == 0) {
            $this->em->flush(); // save unsaved changes
            $this->em->clear(); // clear doctrine managed entities
            gc_collect_cycles(); // PHP garbage collect

            // Note that $this->em->clear() detaches all managed entities,
            // may be you need some; reinit them here
        }
    }

    // don't forget to flush in the end
    $this->em->flush();
    $this->em->clear();
    gc_collect_cycles();
}
_

テーブルが非常に大きい場合は、findAllを使用しないでください。イテレータの使用- http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/batch-processing.html#iterating-results

12
luchaninov
  1. SQLロガーをnullに設定します

$em->getConnection()->getConfiguration()->setSQLLogger(null);

  1. gc_collect_cycles()の後に関数$em->clear()を手動で呼び出す

$em->clear(); gc_collect_cycles();

zend.enable_gc を1に設定することを忘れないでください。または、使用前に gc_enable() を手動で呼び出してください gc_collect_cycles()

  1. 追加 --no-debugコマンドをコンソールから実行する場合。
9
Sergii Smirnov

doctrineからsymfonyの開発者自身がベルリンに住んでいます-彼らは、大規模なバッチでは、ormを使用すべきではないと言っています。.ものを構築するのは効率的ではありませんOOPのように

..ええ..多分彼らは正しいxDです

4
MonocroM

標準のDoctrine2ドキュメントに従って、エンティティを手動でクリアまたはデタッチする必要があります。

それに加えて、プロファイリングが有効になっている場合(デフォルトの開発環境など)。 Symfony2のDoctrineBundleは、いくつかのロガーがかなりのメモリを使用するように設定します。 canロギングを完全に無効にしますが、必須ではありません。

興味深い副作用は、ロガーがDoctrine ORMとDBALの両方に影響することです。ロガーの1つは、デフォルトのロガーサービスを使用するサービスのメモリ使用量が増加します。これらすべてを無効にすることが理想的です。プロファイラはまだ使用されていないため、コマンドで。

Symfony2の他の部分でプロファイリングを有効にしたまま、メモリを大量に消費するロガーを無効にする方法は次のとおりです。

$c = $this->getContainer();
/* 
 * The default dbalLogger is configured to keep "stopwatch" events for every query executed
 * the only way to disable this, as of Symfony 2.3, Doctrine Bundle 1.2, is to reinistiate the class
 */

$dbalLoggerClass = $c->getParameter('doctrine.dbal.logger.class');
$dbalLogger = new $dbalLoggerClass($c->get('logger'));   
$c->set('doctrine.dbal.logger', $dbalLogger);

// sometimes you need to configure doctrine to use the newly logger manually, like this
$doctrineConfiguration = $c->get('doctrine')->getManager()->getConnection()->getConfiguration();
$doctrineConfiguration->setSQLLogger($dbalLogger);

/*
 * If profiling is enabled, this service will store every query in an array
 * fortunately, this is configurable with a property "enabled"
 */
if($c->has('doctrine.dbal.logger.profiling.default'))
{
    $c->get('doctrine.dbal.logger.profiling.default')->enabled = false;
}

/*
 * When profiling is enabled, the Monolog bundle configures a DebugHandler that 
 * will store every log messgae in memory. 
 *
 * As of Monolog 1.6, to remove/disable this logger: we have to pop all the handlers
 * and then Push them back on (in the correct order)
 */
$handlers = array();
try
{   
    while($handler = $logger->popHandler())
    {
        if($handler instanceOf \Symfony\Bridge\Monolog\Handler\DebugHandler)
        {
            continue;
        }
        array_unshift($handlers, $handler);
    }
}
catch(\LogicException $e)
{
    /*
     * As of Monolog 1.6, there is no way to know if there's a handler
     * available to pop off except for the \LogicException that's thrown.
     */
    if($e->getMessage() != 'You tried to pop from an empty handler stack.')
    {
        /*
         * this probably doesn't matter, and will probably break in the future
         * this is here for the sake of people not knowing what they're doing
         * so than an unknown exception is not silently discarded.
         */

        // remove at your own risk
        throw $e;
    }
}

// Push the handlers back on
foreach($handlers as $handler)
{
    $logger->pushHandler($handler);
}
3
Reece45

Doctrine=バッチ処理 here )でSymfonyコンソールコマンドを使用するためのヒントを投稿しました。

0
Collin Krawll

存在するすべてのDoctrineキャッシュを無効にしてみてください。APC/キャッシュをキャッシュとして使用していない場合は、メモリが使用されます)。

クエリキャッシュの削除

$qb = $repository->createQueryBuilder($a);
$query = $qb->getQuery();
$query->useQueryCache(false);
$query->useResultCache(false);
$query->execute();

グローバルに無効にする方法はありません

また、これは役立つかもしれないクリアの代替手段です( here から)

$connection = $em->getCurrentConnection();
$tables = $connection->getTables();
foreach ( $tables as $table ) {
    $table->clear();
}
0
james_t