IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

PHP & Base de données Discussion :

Mémoire maximale atteinte et fin de script


Sujet :

PHP & Base de données

  1. #1
    Membre habitué
    Homme Profil pro
    Développeur Web
    Inscrit en
    avril 2014
    Messages
    190
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : avril 2014
    Messages : 190
    Points : 149
    Points
    149
    Par défaut Mémoire maximale atteinte et fin de script
    Bonjour,

    Pour une application permettant de générer des fichiers à partir de bases de données, j'ai un problème de mémoire que je n'arrive pas à résoudre. J'ai essayé d'améliorer le plus possible la gestion de la mémoire dans le script mais j'arrive à un point où je ne vois pas ce qui peut générer ce type d'erreur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Fatal error:  Allowed memory size of 536870912 bytes exhausted (tried to allocate 528384 bytes)
    Le script utilise principalement une fonction générateur permettant d'extraire des données d'une base de donnée partie par partie et je vide les variables tableaux résultats à chaque boucle, mais après plusieurs dizaines de minutes j'ai ce problème de mémoire vive.
    Je ne comprends vraiment pas pourquoi puisqu'à chaque boucle je vide les variables contenant le plus de données :

    $array=NULL;
    unset($array);
    Ca a amélioré la mémoire disponible, mais lorsque je traite 500 000 lignes par paquet de 1000, j'arrive vers la fin à ce problème de mémoire vive.

    Je ne vois pas l'utilité d'augmenter la taille maximale de mémoire vive puisque normalement le script php devrait se limiter à 200-300 Mo de données utilisées/détruites environ.

    Pourriez vous m'indiquer si il existe un moyen sûr de limiter au minimum l'utilisation de la mémoire vive du script PHP ?

    Merci.

  2. #2
    Membre habitué
    Homme Profil pro
    Développeur Web
    Inscrit en
    avril 2014
    Messages
    190
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : avril 2014
    Messages : 190
    Points : 149
    Points
    149
    Par défaut
    Peut être en rapport avec le problème de mémoire, le script a fini par se figer (en ligne de commande) après avoir fait une requête sur la base Mysql... plus rien ne s'affiche... La requête en elle même ne pose aucun problème visiblement c'est php...

  3. #3
    Membre averti
    Profil pro
    Administrateur
    Inscrit en
    mai 2008
    Messages
    217
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations professionnelles :
    Activité : Administrateur
    Secteur : Industrie

    Informations forums :
    Inscription : mai 2008
    Messages : 217
    Points : 398
    Points
    398
    Par défaut
    536.870912 Mo de mémoire

    Je pense que vous cherchez à traiter trop de données,
    Déjà en terme de performance, cela devrait forcement ralentir votre script.
    Pourquoi ne pas traiter des blocs plus petits, exemple 500 au lieu de 1000

    Avez-vous des appels de fonctions dans votre boucle ? Une fonction recursive par exemple ?
    Il est aussi possible que le chargement des fichiers que vous créer en mémoire puisse y être pour quelque chose.

    Difficile de vous aider sans voir le script.

  4. #4
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    mars 2009
    Messages
    2 309
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : mars 2009
    Messages : 2 309
    Points : 5 049
    Points
    5 049
    Par défaut
    Le script utilise principalement une fonction générateur permettant d'extraire des données d'une base de données...
    Pour en faire quoi? Montre ton script.
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  5. #5
    Membre habitué
    Homme Profil pro
    Développeur Web
    Inscrit en
    avril 2014
    Messages
    190
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : avril 2014
    Messages : 190
    Points : 149
    Points
    149
    Par défaut
    Bonjour,

    En ce qui concerne le blocage de script, j'ai trouvé la raison : en deux mots ma table comportait des valeurs incohérentes, donc lorsque j'interrogeais la table après une mise à jour dans une boucle (mise à jour qui en fait échouait car impossible d'après mon programme), la requête retournait toujours le même résultat .. Il était nécessaire que je supprime ces lignes qui ne devaient pas exister.

    Je vais essayer de synthétiser. J'ai une table avec des données fonctions de la date (de 1973 à 2021 ) à mettre à jour avec en moyenne une valeur toutes les 15 / 30 minutes (soit 8760*2*47=823000 enregistrement grosso modo). le processus prélève les données sources dans cette même table et dans le même temps le processus balaie toute la table pour chaque date et met à jour les lignes si celles ci sont effectivement à modifier (création ou mise à jour).
    C'est le balayage et la mise à jour de la table qui au bout d'un certain temps prend toute la mémoire allouée pour le script.
    Pour balayer toutes les lignes j'ai fait un balayage par parquet en utilisant les générateurs, ici 1 paquet = 5000 lignes et comme la base est en train d'être mise à jour l'offset initial est toujours 0 (j'ai allégé la liste des options qui rendent la lecture un peu longue) :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
        function retourne_tableau_bd_optim_generator($sql,$option_val)
        {
     
            $option_val["nombre_lignes_max_par_requete"]=5000; //nombr ede lignes maximum par requetes par exemple
     
            $nb_lignes=0;
            $nb_pass=0;  
     
            while($nb_lignes==$nb_pass*$option_val["nombre_lignes_max_par_requete"]){
     
    	        $sql_t=$sql." LIMIT 0,".$option_val["nombre_lignes_max_par_requete"];
     
    	        $r = mysqli_query($GLOBALS['base'],$sql_t) or exit(mysqli_error($GLOBALS['base']));
     
    	        while( $donnee = mysqli_fetch_array($r,MYSQLI_ASSOC))  {
    				$nb_lignes++;
    				yield $donnee;
    	       	}
    	       	$donnee=NULL;unset($donnee);
    	       	$nb_pass++;
    			mysqli_free_result($r);				
    		}
     
     
        return $nb_lignes;
        }

    j'appelle la fonction avec :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $raw_data_source_bd=retourne_tableau_bd_optim_generator($sql,array(""));
    Ensuite commence une boucle pour chaque valeur de dates, permettant de contrôler et mettre à jour les données.
    Dans cette boucle je lis les données de la table avec :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $raw_data_source_bd->current()["date_locale"];
    J'extrais des données "sources":
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $data=$raw_data_source_bd->current()["data_sources"];
    Ces données sont traitées pour être mise dans un tableau (1 ligne) temporaire qui servira à mettre à jour la base de données. ce tableau est ensuite détruit puis on recommence la boucle pour une nouvelle date, etc...



    Cette fonction marche parfaitement, et j'ai fait me semble-t il le max pour éviter d'occuper de façon croissante la place de la mémoire disponible.

    La question porte essentiellement sur l'utilisation d'un générateur, est ce que les données renvoyées sont stockées quelque part ? Si oui comment les supprimer périodiquement ?

  6. #6
    Modérateur
    Avatar de grunk
    Homme Profil pro
    Architecte Web / Android
    Inscrit en
    août 2003
    Messages
    6 247
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Architecte Web / Android
    Secteur : Industrie

    Informations forums :
    Inscription : août 2003
    Messages : 6 247
    Points : 17 941
    Points
    17 941
    Par défaut
    En théorie avec un générateur tu ne consommes pas plus que ce que consomme un "tour" du générateur.

    Tu peux essayer de voir où la mémoire est "perdue" en utilisant memory_get_usage()
    Pry Framework php5 | N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  7. #7
    Membre habitué
    Homme Profil pro
    Développeur Web
    Inscrit en
    avril 2014
    Messages
    190
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : avril 2014
    Messages : 190
    Points : 149
    Points
    149
    Par défaut
    J'ai finalement fait plusieurs mesures de mémoires à différents points à l'aide de la fonction memory_get_usage() sur de nombreuses itérations. La tendance globale c'est que unset() n'agit pas instantanément, mais par cycle.
    Ensuite dans une même boucle, si chaque variable n'est pas détruite avec unset(), les données ne sont pas totalement supprimées de la mémoire vive, puisque j'ai la mémoire qui globalement est une courbe croissante même si la courbe est en "dents de scie".
    Enfin j'ai fini par faire appel à la fonction gc_collect_cycles() qui permet réellement de supprimer les données qui ne sont plus utilisées par le programme : la courbe revient grosso modo à sa valeur minimale correspondant aux données réellement utilisées par le programme.
    En conclusion gc_collect_cycles() semble une bonne solution par son efficacité, même si unset() est finalement plus précis.

  8. #8
    Modérateur
    Avatar de grunk
    Homme Profil pro
    Architecte Web / Android
    Inscrit en
    août 2003
    Messages
    6 247
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Architecte Web / Android
    Secteur : Industrie

    Informations forums :
    Inscription : août 2003
    Messages : 6 247
    Points : 17 941
    Points
    17 941
    Par défaut
    Par contre il est fort probable que forcer le garbage collector est un coût non négligeable sur les performance.
    Pry Framework php5 | N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  9. #9
    Membre habitué
    Homme Profil pro
    Développeur Web
    Inscrit en
    avril 2014
    Messages
    190
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : avril 2014
    Messages : 190
    Points : 149
    Points
    149
    Par défaut
    Je ne saurais répondre avec précision, mais tout dépend de la fréquence d'utilisation. Dans mon car je l'utilise après avoir traité un certain nombre de données et pas systématiquement, donc l'appel se fait une fois toutes les 5000 données traitées. Alors qu'en utilisant unset pour chaque variable utilisées, cela nécessite d'exécuter 20-30 fois unset() pour chaque boucle avec l'incertitude de les avoir toutes. Un test serait nécessaire pour voir qu'est ce qui est vraiment plus rapide 30*5000 fois unset ou une fois de temps en temps gc_collect_cycles() !?

  10. #10
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    mars 2009
    Messages
    2 309
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : mars 2009
    Messages : 2 309
    Points : 5 049
    Points
    5 049
    Par défaut
    Si j'ai bien compris (ce qui n'est pas sûr), à un moment de ton script tu as tes 5000 données stockées dans un tableau pour pouvoir les traiter. Dans ce cas, est-ce qu'il ne serait pas possible d'effectuer ce traitement directement avec MySQL (ce qui éviterait le découpage). Autre chose, as-tu essayer de remplacer le tableau par une des datastructures (qui offrent un meilleur contrôle de la mémoire) pour voir ce que ça donne?
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  11. #11
    Membre habitué
    Homme Profil pro
    Développeur Web
    Inscrit en
    avril 2014
    Messages
    190
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : avril 2014
    Messages : 190
    Points : 149
    Points
    149
    Par défaut
    Non, effectivement ma requête principale traite un volume de 5000 lignes d'une table sql mais qui sont récupérées par un générateur php donc pas de tableau de 5000 lignes, même si elles sont bien stockées quelque part ces données... effectivement, mais ça n'apparaît pas dans le script.
    Pour le traitement par SQL c'est une très bonne idée dans l'absolue, je travaille en général dans ce sens , pour l'instant j'utilise php. L'avantage de php c'est de pouvoir faire une routine pour la consolidation / mise à jour d'une base de données complète en utilisant différentes fonctions de traitement, ce qui me semble plus simple qu'en SQL même si c'est moins performant.
    Pour le contrôle de la mémoire je ne connais pas les datastructure.
    Sinon je pense à une autre solution qui consiste à utiliser les flux php://temp ou php://memory permettant un contrôle total de la mémoire (max notamment) moyennant quelques effort supplémentaires de programmation peut être même la solution ultime et même un gain de performance (?), à cogiter !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Signal sonore en fin de script
    Par saih_tam dans le forum Simulink
    Réponses: 2
    Dernier message: 06/03/2009, 18h55
  2. mémoire maximale d'un programme c
    Par Invité1 dans le forum C
    Réponses: 16
    Dernier message: 26/08/2008, 20h59
  3. Libération mémoire + variable inutile en fin de code
    Par kenshi240683 dans le forum GTK+ avec C & C++
    Réponses: 2
    Dernier message: 12/06/2008, 17h44
  4. Appeler une fonction en fin de script
    Par Oprichnik dans le forum Langage
    Réponses: 4
    Dernier message: 26/08/2007, 23h42
  5. Réponses: 13
    Dernier message: 10/11/2006, 09h24

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo