Question

Mon application PHP comporte un script d'importation permettant d'importer des enregistrements.

Pour le moment, il importe depuis un fichier CSV. Il lit chaque ligne du fichier CSV, une ligne à la fois, à l’aide de fgetcsv, et pour chaque ligne, il effectue beaucoup de traitements sur cet enregistrement, y compris les requêtes de base de données, puis passe à la page suivante. ligne suivante. Il ne devrait pas être nécessaire de continuer à accumuler plus de mémoire.

Après environ 2 500 enregistrements importés, PHP meurt, affirmant qu'il a dépassé sa limite de mémoire (environ 132 Mo).

Le fichier CSV lui-même n’est que de quelques mégas - l’autre traitement en cours fait beaucoup de comparaisons de chaînes, de diffs, etc. J'ai une énorme quantité de code qui fonctionne dessus et il serait difficile de trouver une "le plus petit échantillon reproducteur".

Quels sont les bons moyens de trouver et de résoudre un tel problème?

La cause du problème trouvé

J'ai une classe de débogage qui enregistre toutes les requêtes de ma base de données pendant l'exécution. Ainsi, ces chaînes de SQL, d'une longueur d'environ 30 Ko, restaient en mémoire. Je réalise que cela ne convient pas aux scripts conçus pour fonctionner pendant longtemps.

Il peut y avoir d'autres sources de fuites de mémoire, mais je suis à peu près sûr que c'est la cause de mon problème.

Était-ce utile?

La solution

Il serait utile de consulter le code, mais si vous souhaitez le déboguer vous-même, consultez Xdebug , cela vous aidera à établir le profil de votre application.

Bien sûr, selon ce que vous faites, il est possible que de la mémoire s’accumule, bien que 132 Mo semble déjà élevé pour 2500 enregistrements. Bien entendu, vous pouvez modifier votre limite de mémoire . dans php.ini si nécessaire.

Quelle est la taille du fichier CSV que vous lisez? Et quels objets et quel type de traitement faites-vous pour cela?

Autres conseils

Si vous soupçonnez en fait que votre script ne se bloque qu'une ou deux fuites de mémoire, vous devez suivre les étapes suivantes:

  • Remplacez limite_mémoire par quelque chose de petit, comme 500 Ko
  • Mettez en commentaire toutes les étapes de traitement appliquées à chaque ligne sauf une.
  • Exécutez le traitement limité sur l'intégralité du fichier CSV et voyez s'il peut être terminé.
  • Ajoutez progressivement de nouvelles étapes et observez si l'utilisation de la mémoire augmente.

Exemple:

ini_set('memory_limit', 1024 * 500);
$fp = fopen("test.csv", 'r');
while($row = fgetcsv($fp)) {
    validate_row($row);         // step 1: validate
    // add these back in one by one and keep an eye on memory usage
    //calculate_fizz($row);     // step 2: fizz
    //calculate_buzz($row);     // step 3: buzz
    //triangulate($row);        // step 4: triangulate
}
echo "Memory used: ", memory_get_peak_usage(), "\n";

Le pire des scénarios est que toutes toutes vos étapes de traitement soient moyennement inefficaces et que vous deviez toutes les optimiser.

Cela dépend de la manière dont vous effacez les variables une fois que vous en avez terminé.

On dirait que vous avez terminé avec le dossier mais que vous stockez toujours les informations quelque part. Utilisez unset () pour effacer les variables en cas de doute.

Veuillez fournir un exemple de code de reproduction minimal pour savoir où se trouve toute cette mémoire si cela ne vous aide pas.

BTW, produire le plus petit exemple de code reproduisant le problème est une excellente technique de débogage, car elle vous oblige à repasser le code à nouveau avec précaution.

vous pouvez essayer une installation locale de php5.3 et appeler http://www.php.net/manual/en/function.gc-collect-cycles.php.

  

gc_collect_cycles & # 8212; Force la collecte de tous les cycles de déchets existants

si la situation s'améliore, vous avez au moins vérifié le (s) problème (s).

Comment lisez-vous le fichier? Si vous utilisez fread / filegetcontents ou d'autres fonctions de ce type, vous allez utiliser toute la taille du fichier (ou le volume que vous chargez avec fread) en mémoire, car l'ensemble du fichier est chargé au moment de l'appel. Toutefois, si vous utilisez fgetcsv si vous ne pouvez lire qu'une ligne à la fois, cela dépend de la longueur de la ligne. considérablement plus facile sur votre mémoire.

Assurez-vous également de réutiliser autant de variables que possible sur chaque boucle. Vérifiez qu’il n’existe aucun tableau contenant de grandes quantités de données.

Comme dernière remarque, assurez-vous également que vous ouvrez votre fichier avant votre boucle, puis que vous le fermez après:

$fh = fopen(...);
while(true)
{
//...
}
fclose($fh);

Vous ne voulez pas vraiment faire ceci:

while(true)
{
$fh = fopen(...);
//...
fclose($fh);
}

Et comme d’autres l’ont dit, ce sera difficile à dire sans voir un code.

Il est difficile de dire la cause sans voir de code. Cependant, un problème typique est celui des références récursives, c.-à-d. objet A pointe vers l'objet B et inversement, ce qui peut provoquer l'échec du CPG.

Je ne sais pas comment vous traitez actuellement le fichier, mais vous pouvez essayer de ne lire que le fichier, une ligne à la fois. Si vous lisez tout le fichier à la fois, il risque de consommer plus de mémoire.

C’est en fait l’une des raisons pour lesquelles je préfère souvent Python pour les tâches de traitement par lots.

Pouvez-vous changer votre memory_limit dans votre php.ini?

De plus, le fait de définir des variables non définies ($ var) sur des variables permettrait-il de libérer de la mémoire? Est-ce que $ var = null peut aussi aider?

Voir aussi cette question: Quoi de mieux? lors de la libération de mémoire avec PHP: unset () ou $ var = null

J'avais le même problème, et cela était également dû au profilage de la base de données (Zend_Db_Profiler_Firebug). Dans mon cas, il coulait 1 Mo par minute. ce script était censé durer plusieurs jours, de sorte qu'il se planterait en quelques heures.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top