PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Große Dateien einlesen


FabianWesner
29.05.2007, 10:44:25
Hallo,

ich habe folgendes Problem zu lösen: Mein System soll große CSV-Dateien einlesen (1-bis 50 MB).

Mir ist wichtig, dass der Server dabei trotzdem ansprechbar bleibt und das Einlesen nicht vom Benutzer abgebrochen werden kann (z.B. durch Verlassen der Webseite)

Mit Java wäre das kein großes Problem:

Der Benutzer lädt seine Datei hoch und verlässt die Webseite
Ein Cronjob startet irgentwann ein Javaprogramm, welches -langsam- die CSV-Datei einliest und in die Datenbank speichert, ohne dabei den Server auszulasten


Nun möchte ich das mit PHP machen, also ohne virtual machine. Daher meine Fragen:

Ist es möglich ein PHP-Script auszuführen (z.B. per Cronjob), ohne das eine Webseite aufgerufen wird ?
Ist es möglich ein PHP-Script über einen längeren Zeitraum Daten in die Datenbank speichern zu lassen, ohne das ich die allgemeine 30-Sekunden Sperre aufheben muss?
Ist es möglich eine Datei stückchenweise einzulesen und zu speichern? Ich könnte alles mit fgetcsv() in ein Array packen und dieses danach in die Datenbank schreiben, aber dabei wird massiv Speicher belegt. Lieber würde ich das Stück für Stück machen.
Wie würdet ihr das Problem lösen?


Vielen Dank für eure Tipps und Kommentare!!!

meikel (†)
29.05.2007, 11:07:55
ich habe folgendes Problem zu lösen: Mein System soll große CSV-Dateien einlesen (1-bis 50 MB).
Ist kein Problem, wenn Du das File zeilenweise verarbeitest und set_time_limit(0) gestattet ist.

> Daher meine Fragen:
> Ist es möglich ein PHP-Script auszuführen (z.B. per Cronjob), ohne das eine Webseite aufgerufen wird ?

Ja. Dem Script ist das egal.

> Ist es möglich ein PHP-Script über einen längeren Zeitraum Daten in die Datenbank speichern zu lassen, ohne das ich die allgemeine 30-Sekunden Sperre aufheben muss?

Nein.

> Ist es möglich eine Datei stückchenweise einzulesen und zu speichern?

Ja. Das ist bei 50 MB zwingend nötig.

> Ich könnte alles mit fgetcsv() in ein Array packen und dieses danach in die Datenbank schreiben, aber dabei wird massiv Speicher belegt.

Das wäre unklug. Bastele in der Schleife aus jeder CSV Zeile einen INSERT String und schick den zum MySQL Server.

Das Problem ist 30-sec-Grenze.

FabianWesner
29.05.2007, 11:25:39
Hallo meikel,

set_time_limit(0) kannte ich garnicht. Das sollte klappen :-)

Macht es Sinn die Schleife (while(($row = fgetcsv()) mit sleep(1) o.ä. zu bremsen? Der Server darf auf keinen Fall blockiert werden, wenn jemand eine 50 MB-Datei einstellt.

Danke!
Fabian

FabianWesner
29.05.2007, 14:12:18
Macht es Sinn die Schleife (while(($row = fgetcsv()) mit sleep(1) o.ä. zu bremsen? Der Server darf auf keinen Fall blockiert werden, wenn jemand eine 50 MB-Datei einstellt.

Vielleicht interessiert es jemanden:
Ich habe ein wenig experimentiert. Es macht scheinbar Sinn die Schleife zu verzögern.

Eine 30 MB-Datei benötigt bei konstanter 50% CPU-Last 148s ohne Verzögerung. Mit Verzögerung durch usleep(1) bei jedem 10ten Schleifendurchlaf dauert es zwar 260s, aber dafür bleibt die CPU bei zwischen 25-36%!

meikel (†)
29.05.2007, 14:49:51
set_time_limit(0) kannte ich garnicht. Das sollte klappen :-)
a. selbst ich finde im Handbuch immer wieder mal was neues
b. fein.

Ratsam ist es, wenn Du den User bzw. dessen Client immer mal was zum Anzeigen bietest, weil sonst der Browser meutert.

Macht es Sinn die Schleife (while(($row = fgetcsv()) mit sleep(1) o.ä. zu bremsen?
Nein. Sleep klaut der Maschine nur zusätzlich noch Zeit.

Der Server darf auf keinen Fall blockiert werden, wenn jemand eine 50 MB-Datei einstellt.
Erledige das anders:
1. richte für jeden CSV Lieferanten einen separaten FTP Zugang ein. Ist unter Linux kein Problem. 50MB schickt man nicht per HTTP an ein PHP Script:
upload_max_filesize, memory_limit und post_max_size müßten dann auf 50M gesetzt werden.
2. erstelle ein PHP CLI Script, welches direkt von cron mit Root-Rechten aufgerufen wird, sich die CSV Files wegfischt und den Kram in die Datenbank importiert. Den Import könntes Du dann auch so gestalten:
das Script erstellt aus dem CSV File ein SQL File, bei dem jede Zeile mit ";\n" endet.
Dieses SQL File kannste dann via Shellscript ganz bequem und vor allen Dingen schnell in die Datenbank importieren:
shell_exec('/usr/bin/mysql -h localhost -u datenbankbenutzer -pGEHEIMES_PASSWORT datenbankname < /pfad/zum/sqlfile.sql');
Beachte, daß cron Scripte, die über die Shell aufgerufen werden, lautlos arbeiten müssen, weil sonst cron den Text geschwätziger Shellscripte per email an den Root schickt.

FabianWesner
29.05.2007, 15:31:29
sehr gute Anleitung... Danke!