Mes scripts PHP me donnent une erreur de serveur interne de 500 quand ils sont intenses

En bref, si je divise un script PHP en petits morceaux, je pourrai éventuellement exécuter tout mon code. Cependant, j’ai maintenant un script qui lit un énorme fichier CSV et insère chaque ligne dans une firebase database MySQL. Plutôt que de devoir passer par le processus onéreux de fractionnement du fichier chaque fois que je veux mettre à jour mon site, je veux simplement que ce script fonctionne comme je le sais.

Je l’ai fait pour insérer environ 10 000 lignes auparavant sur un serveur Web différent, mais il y en a au moins 7 fois dans le fichier, et ça craque avant que ça soit fait.

Ainsi, l’histoire est que, sur un serveur, il s’arrête avant d’être censé le faire, et sur un autre, il ne fonctionne pas du tout… il se contente d’atteindre une erreur 500 après environ 30 secondes.

Le journal des erreurs Apache me fournit ces lignes lorsque le script meurt:

[Tue Aug 23 13:09:04 2011] [warn] [client 71.168.85.72] mod_fcgid: read data timeout in 40 seconds [Tue Aug 23 13:09:04 2011] [error] [client 71.168.85.72] Premature end of script headers: newcsvupdater.php 

J’ai au début du script ces deux lignes:

 set_time_limit(0); ini_set('memory_limit','256M'); 

parce qu’auparavant j’avais une erreur fatale d’allocation de mémoire, car apparemment, séparer un grand fichier en tableaux nécessite beaucoup de mémoire.

Voici le code d’insertion:

 $file = "./bigdumbfile.csv"; // roughly 30mb $handle = fopen($file, r); $firstentry = 0; while($csv = fgetcsv($handle)) { if($firstentry == 0) { $firstentry++; // skips the top row of field names } else { // unimportant conditional code omitted $checkforexisting = mysql_query("SELECT * FROM DB_TABLE WHERE ". "id_one = '".$csv[0]."' AND id_two = '".$csv[2]."'"); $checknum = mysql_num_rows($checkforexisting); if($checknum == 0) { if(!mysql_query("INSERT INTO DB_TABLE ". "(id_one, data_one, id_two, data_two, ". /* so on for 22 total fields */") VALUES ('".addslashes($csv[0])."', '". addslashes($csv[1])."', '". addslashes($csv[2])."', '". addslashes($csv[3])."' "/* ditto, as above */)) { exit("
" . mysql_error()); } else { print_r($csv); echo " insert complete

"; } } } } echo "
DB_TABLE UPDATED";

J’ai dû séparer des tâches importantes à cause de cela, et j’en ai assez. Je suis sûr que je me trompe beaucoup, car je suis totalement autodidacte et j’écris généralement ce qui équivaut à du spaghetti, alors ne vous retenez pas.

Pour augmenter la limite de temps de votre script, vous devez modifier la configuration de l’hôte virtuel pour votre site:

http://www.moe.co.uk/2009/08/17/php-running-under-mod_fcgid-read-data-timeout-in-40-seconds-on-plesk/

(le délai d’expiration de mod_fcgid remplace le délai d’expiration de PHP)

Pour rendre votre script plus rapide (vous n’avez peut-être pas besoin d’effectuer l’étape ci-dessus, ce qui pourrait ne pas être possible avec l’hébergement partagé), essayez ceci:

Préparez toutes les informations à insérer dans avancé pour effectuer une insertion en bloc. La requête devrait ressembler à ceci:

 INSERT IGNORE INTO (id_one, data_one, id_two, data_two) VALUES (1, 'apple', 3, 'banana'), (4, 'pear', 5, 'orange) 

La partie IGNORE devrait avoir le même effet de vérifier à l’avance si l’enregistrement existe déjà (s’il le fait, il ne sera tout simplement pas inséré et il continuera jusqu’au prochain).

Vous pouvez insérer des lots de valeurs à l’aide de SQL, ce qui réduit le temps nécessaire à l’exécution de votre requête (l’aller-retour est probablement votre goulot d’étranglement).

 INSERT INTO table (cola,colb...) VALUES (vala,valb...), (valc,vald...) 

La plupart du temps, lorsque vous effectuez des insertions énormes comme celle-ci, vous voulez le faire de manière asynchrone, ce qui signifie que vous sauvegardez un fichier quelque part,

Je vois aussi que vous faites une vérification de l’existence avant de faire l’insertion. Vous voudrez peut-être envisager de sélectionner des lignes qui “pourraient” correspondre, puis effectuer la vérification côté PHP (en utilisant un hachage) plutôt que d’exécuter cette requête à chaque fois.

Il semble que vous ayez du temps mort sur Apache et non sur PHP. La fonction set_time_limit est utilisée pour php-script et apache ne sait rien à ce sujet.

Mon premier instinct serait de faire tout cela sans PHP en utilisant mysqlimport ou mieux encore, LOAD DATA INFILE .

 LOAD DATA INFILE ./bigdumbfile.csv INTO TABLE tbl_name;