web-dev-qa-db-fra.com

Traite un très gros fichier csv sans dépassement de délai ni erreur de mémoire

En ce moment, j'écris un script d'importation pour un très gros fichier CSV. Le problème est que la plupart du temps, il s’arrête après un certain temps en raison d’un délai ou d’une erreur de mémoire.

Mon idée était maintenant d’analyser le fichier CSV par étapes de "100 lignes" et après 100 lignes, le script s’appelle automatiquement. J'ai essayé d'y parvenir avec en-tête (emplacement ...) et de passer la ligne actuelle avec get mais cela n'a pas fonctionné comme je le voulais.

Y a-t-il une meilleure façon de procéder ou quelqu'un a-t-il une idée de la façon de se débarrasser de l'erreur de mémoire et du délai d'attente?

23
Julian

J'ai utilisé fgetcsv pour lire un fichier csv de 120 Mo en mode flux (c'est bien anglais?). Cela se lit ligne par ligne puis j'ai inséré chaque ligne dans une base de données. Ainsi, une seule ligne est conservée en mémoire à chaque itération. Le script avait encore besoin de 20 min. courir. Peut-être que j'essaierai Python la prochaine fois… N'essayez pas de charger un énorme fichier csv dans un tableau, cela consommerait beaucoup de mémoire.

// WDI_GDF_Data.csv (120.4MB) are the World Bank collection of development indicators:
// http://data.worldbank.org/data-catalog/world-development-indicators
if(($handle = fopen('WDI_GDF_Data.csv', 'r')) !== false)
{
    // get the first row, which contains the column-titles (if necessary)
    $header = fgetcsv($handle);

    // loop through the file line-by-line
    while(($data = fgetcsv($handle)) !== false)
    {
        // resort/rewrite data and insert into DB here
        // try to use conditions sparingly here, as those will cause slow-performance

        // I don't know if this is really necessary, but it couldn't harm;
        // see also: http://php.net/manual/en/features.gc.php
        unset($data);
    }
    fclose($handle);
}
49
feeela

Je trouve le téléchargement du fichier et l'insertion à l'aide de la requête LOAD DATA LOCAL de mysql une solution rapide, par exemple:

    $sql = "LOAD DATA LOCAL INFILE '/path/to/file.csv' 
        REPLACE INTO TABLE table_name FIELDS TERMINATED BY ',' 
        ENCLOSED BY '\"' LINES TERMINATED BY '\r\n' IGNORE 1 LINES";
    $result = $mysqli->query($sql);
15
Craigo

Si vous ne vous souciez pas de la durée et de la quantité de mémoire dont vous avez besoin, vous pouvez simplement augmenter les valeurs de ce script. Ajoutez simplement les lignes suivantes en haut de votre script:

ini_set('memory_limit', '512M');
ini_set('max_execution_time', '180');

Avec la fonction memory_get_usage () , vous pouvez savoir combien de mémoire votre script a besoin pour trouver une valeur correcte pour memory_limit.

Vous pouvez également vouloir regarder fgets () qui vous permet de lire un fichier ligne par ligne. Je ne sais pas si cela prend moins de mémoire, mais je pense vraiment que cela fonctionnera. Mais même dans ce cas, vous devez augmenter la valeur max_execution_time.

13
2ndkauboy

Il semble y avoir une énorme différence entre fgetcsv () et fgets () en ce qui concerne la consommation de mémoire . Un simple CSV avec une seule colonne dépassait la limite de mémoire de 512 M pour seulement 50000 enregistrements avec fgetcsv () et prenait 8 minutes avant. signale ça.

Avec fgets (), le traitement de 649175 enregistrements n’a pris que 3 minutes et mon serveur local n’avait même pas le souffle coupé.

Donc, mon conseil est d’utiliser fgets () si le nombre de colonnes de votre csv est limité. Dans mon cas, fgets () a retourné directement la chaîne de la colonne 1 . Pour plus d'une colonne, vous pouvez utiliser explode () dans un tableau jetable que vous avez désactivé () après chaque opération d'enregistrement . Réponse 3 @ndkauboy

1
rawdesk.be