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

en fait, j'ai beaucoup de mal avec la combinaison de symfony2 et doctrine2. Je dois faire face à d'énormes ensembles de données (environ 2-3 millions d'écrire et de lire) et de faire beaucoup d'efforts supplémentaires pour éviter de manquer de mémoire.

j'ai trouvé 2 points principaux, qui "fuient" la mémoire (ils ne fuient pas vraiment, mais allouent beaucoup)

  1. L'Entitymanager entité de stockage (je ne sais pas le vrai nom de celui - ci) il semble comme il garde toutes les entités traitées et vous devez nettoyer ce stockage régulier avec

    $entityManager->clear()
  2. la Doctrine QueryCache - il cache toutes les requêtes utilisées et la seule configuration que j'ai trouvé était que vous êtes en mesure de décider quel type de Cache vous voulez utiliser. Je n'ai pas trouvé un mondial de désactiver ni utile drapeau pour chaque requête pour le désactiver. Donc, habituellement, le désactiver pour chaque objet de requête avec la fonction

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

.. c'est tout ce que j'ai compris maintenant.. mes questions sont:

y a-t-il un moyen facile de nier Certains objets de L'Entitymanagerstorage? Est-il un moyen de régler le querycache utilisation dans l'entitymanager? Puis-je configurer ces comportements de cache quelque chose dans la configuration de la doctrine symonfony?

Serait très cool si quelqu'un a quelques conseils sympas pour moi.. sinon, cela peut aidé certains recrue..

cya

43
demandé sur MonocroM 2012-03-14 13:32:51

8 réponses

un peu tard, mais je pense que je viens de trouver un thread sur Google Groups par Benjamin Eberlei qui répond à votre question: comme indiqué par le référence de Configuration de Doctrine par défaut logging de la connexion SQL est mis à la valeur de noyau.debug , donc si vous avez instancié AppKernel avec debug défini à true les commandes SQL sont stockées en mémoire pour chaque itération.

vous devez instancier AppKernel à false , définir logging à false dans votre configuration YML, ou définir le Sqlllogger manuellement à null avant d'utiliser L'EntityManager

$em->getConnection()->getConfiguration()->setSQLLogger(null);
84
répondu Sergi 2012-06-06 11:16:32

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

17
répondu arnaud576875 2012-09-14 11:32:09

1. Désactiver la journalisation et le profilage dans app/config/config.yml

doctrine:
    dbal:
        driver: ...
        ...
        logging: false
        profiling: false

ou dans le code

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

2. Forcer le ramasseur d'ordures . Si vous utilisez activement CPU puis collecteur de déchets attend et vous pouvez vous retrouver sans mémoire bientôt.

d'abord activer la gestion manuelle de la collecte des ordures. Exécutez gc_enable() n'importe où dans le code. Puis lancez gc_collect_cycles() pour forcer garbage collector.

exemple

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();
}

si votre table est très grande, n'utilisez pas findAll . Utiliser itérateur - http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/batch-processing.html#iterating-results

10
répondu luchaninov 2015-11-12 10:23:14
  1. Set SQL enregistreur null

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

  1. fonction D'appel manuel gc_collect_cycles() après $em->clear()

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

N'oubliez pas de mettre zend.enable_gc à 1, ou appeler manuellement gc_enable () avant utilisation gc_collect_cycles ()

  1. ajouter --no-debug option si vous exécutez la commande depuis la console.
9
répondu Sergii Smirnov 2015-06-18 19:59:40

a reçu des nouvelles" drôles " des développeurs de doctrine elle - même sur le Symfony live à berlin-ils disent, que sur les grands lots, nous ne devrions pas utiliser un orm .. il est tout simplement pas efficace de construire des choses comme cela dans oop

.. ouais.. peut-être qu'ils ont raison xD

3
répondu MonocroM 2012-11-23 11:51:51

conformément à la documentation Standard Doctrine2, vous devez nettoyer ou détacher manuellement les entités.

en plus de cela, lorsque le profilage est activé (comme dans l'environnement dev par défaut). Le DoctrineBundle de Symfony2 configure plusieurs loggers utilisant un peu de mémoire. Vous peut désactiver la journalisation complètement, mais il n'est pas nécessaire.

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

voici ce que vous pouvez faire pour désactiver les loggers mémoire intense 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);
}
3
répondu Reece45 2013-09-08 05:32:13

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

Supprimer Le Cache De Requête

$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 à l'échelle mondiale

C'est aussi une alternative à clear qui pourrait aider (de ici )

$connection = $em->getCurrentConnection();
$tables = $connection->getTables();
foreach ( $tables as $table ) {
    $table->clear();
}
0
répondu james_t 2017-05-23 11:54:44

je viens de poster un tas de conseils pour l'utilisation des commandes de la console Symfony avec la Doctrine pour le traitement par lots ici .

0
répondu Collin Krawll 2017-05-23 12:26:19