46 votes

Fuites de mémoire Symfony2 Doctrine2 / dépassement de la limite de mémoire

J'ai beaucoup de mal avec la combinaison de symfony2 et doctrine2. Je dois gérer d'énormes ensembles de données (environ 2 à 3 millions en écriture et en lecture) et je dois faire beaucoup d'efforts supplémentaires pour éviter de manquer de mémoire.

J'ai trouvé 2 points principaux, qui "fuient" de la mémoire (en fait, ils ne fuient pas vraiment, mais allouent beaucoup).

  1. Le stockage d'entités d'Entitymanager (je ne connais pas le vrai nom de celui-ci) semble conserver toutes les entités traitées et vous devez vider ce stockage régulièrement avec

    $entityManager->clear()
  2. Le Doctrine QueryCache - il met en cache toutes les requêtes utilisées et la seule configuration que j'ai trouvée est que vous pouvez décider quel type de cache vous voulez utiliser. Je n'ai pas trouvé de désactivation globale ni de drapeau utile pour chaque requête pour le désactiver. Donc habituellement je le désactive pour chaque objet de requête avec la fonction

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

Donc c'est tout ce que j'ai trouvé pour le moment Mes questions sont :

Existe-t-il un moyen simple de refuser certains objets dans le stockage Entitymanagerstorage ? Existe-t-il un moyen de définir l'utilisation du querycache dans le gestionnaire d'entités ? Est-ce que je peux configurer ce comportement de cache quelque part dans la configuration de Symfony/doctrine ?

Ce serait très cool si quelqu'un avait quelques conseils pour moi sinon cela pourrait aider certains débutants

cya

4 votes

La couche ORM D2 n'est pas vraiment conçue pour le traitement massif par lots. Vous feriez mieux d'utiliser la couche DBAL et de travailler uniquement avec des tableaux.

1 votes

Courir avec --no-debug aide beaucoup (en mode débogage, le profileur conserve des informations sur chaque requête en mémoire).

89voto

Sergi Points 1320

Comme l'indique le Référence de configuration de Doctrine par défaut journalisation de la connexion SQL est défini à la valeur de kernel.debug Ainsi, si vous avez instancié AppKernel avec la valeur debug définie sur vrai les commandes SQL sont stockées en mémoire pour chaque itération.

Vous devriez soit instancier AppKernel pour faux ensemble journalisation a faux dans votre YML de configuration, ou bien définissez manuellement la valeur null de SQLLogger avant d'utiliser l'EntityManager.

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

4 votes

Bravo ! Cela fait des mois que nous nous heurtons à ce problème !

3 votes

C'est le truc avec SF2. Vous realmente Vous devez lire la documentation et le code pour comprendre comment cela fonctionne. L'autre jour, nous avons découvert que nous ne mettions pas en cache les DQL et les métadonnées entre les requêtes. Nous l'avons fait et a fini par avec des demandes deux fois plus rapides qu'avant le changement

4 votes

Cela a été très utile. J'avais une commande console que j'avais écrite (c'était comme une commande de type "démon") qui n'arrêtait pas de manquer de mémoire et d'utiliser l'attribut clear() sur le gestionnaire d'objets n'était pas suffisante. La désactivation de ce logger SQL a fait l'affaire. Cependant, comme j'étais dans une commande console, j'ai dû utiliser $this->getContainer()->get('doctrine')->getEntityManager() d'accéder au gestionnaire d'entités pour faire ça.

18voto

arnaud576875 Points 35281

Essayez d'exécuter votre commande avec --no-debug . En mode débogage, le profileur conserve des informations sur chaque requête en mémoire.

0 votes

Merci, ceci en combinaison avec la désactivation du sqllogging a vraiment aidé.

0 votes

Ceci a résolu un problème avec le moteur de modélisation et twig. L'exécution d'une boucle sur un modèle simple semblait être une fuite de mémoire dans le développement.

0 votes

Je bricolais depuis des heures quand j'ai trouvé ceci... cela a résolu le problème. merci :)

4voto

Dragnic Points 161

J'ai reçu des nouvelles "amusantes" de la part des développeurs de doctrine eux-mêmes lors du symfony live à Berlin - ils disent que pour les gros lots, nous ne devrions pas utiliser un orm il n'est tout simplement pas efficace de construire ce genre de choses dans oop.

ouais peut-être qu'ils ont raison xD

0 votes

Nous devrons probablement emprunter cette voie aussi.... ça craint que même s'ils sont au courant, ils ne s'en occupent pas.

0 votes

Le modèle Mapper qu'ils mettent en œuvre n'est pas vraiment adapté aux performances, mais à une meilleure abstraction et à un meilleur découplage. Je suppose que les problèmes de mémoire peuvent être résolus en utilisant le Doctrine\ORM\Tools\Pagination\Paginator et en se souvenant de $em->clear() de temps en temps, pour décharger de la mémoire des entités déjà parcourues.

3voto

Reece45 Points 1547

Conformément à la documentation standard de Doctrine2, vous devrez effacer ou détacher manuellement les entités.

En outre, lorsque le profilage est activé (comme dans l'environnement de développement par défaut). Le DoctrineBundle de Symfony2 configure plusieurs loggers qui utilisent pas mal de mémoire. Vous pouvez consulter peut désactiver complètement la journalisation, mais ce n'est pas obligatoire.

Un effet secondaire intéressant, est que les loggers affectent à la fois Doctrine ORM et DBAL. L'un des loggers entraînera une utilisation supplémentaire de la mémoire pour tout service qui utilise le service de logger par défaut. L'idéal serait de les désactiver tous dans les commandes, puisque le profileur n'y est pas encore utilisé.

Voici ce que vous pouvez faire pour désactiver les enregistreurs gourmands en mémoire tout en gardant le profilage activé dans d'autres parties de 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);
}

0voto

james_t Points 1528

Essayez de désactiver tous les caches Doctrine qui existent. (Si vous n'utilisez pas APC / autre comme cache, la mémoire est utilisée).

Supprimer le cache des requêtes

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

Il n'y a aucun moyen de le désactiver globalement.

Voici également une alternative à clear qui pourrait aider (de aquí )

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X