2010-12-16 7 views
4

Ich habe ein PHP-Skript, das CSV-Dateien importiert und Zehntausende von Iterationen durchläuft. Wenn das Skript über mehrere Stunden läuft, steigt die Speicherbelegung, und wenn die Datei groß genug ist, verbraucht das Skript so viel Speicher, dass die gesamte Maschine zum Stillstand kommt.Verbessern Speicherverbrauch für PHP-Import-Skript

Im Moment ist die einzige Technik, die ich verwende, unset() alles was ich kann, wenn ich damit fertig bin. Ich habe versucht, den Teil zu isolieren, der am meisten Speicher verbraucht, aber es scheint, als ob jede Funktion in meinem Skript nur noch ein Strohhalm auf dem Rücken des Kamels ist und "so wenig Speicher wie möglich" verwendet.

Was kann ich tun?

Ich habe versucht, in Benchmarking/Profiling-Tools zu suchen, aber ich habe nichts Gutes gefunden. Ich bin auf einer Windows-Maschine, SSHing in eine Linux-Box.

+0

Vielleicht möchten Sie schreiben etwas Code ... – ircmaxell

+1

@ircmaxell Ich bin nicht sicher, dass das hilfreich wäre. 1) Es gibt Tausende von Zeilen, verteilt auf Dutzende von verschiedenen Dateien. 2) Ich frage nach technischem Rat, nicht um jemanden zu bitten, mir die Antwort zu geben. –

+0

@Jason Swett - iterieren Sie alle Zeilen in mehreren CSV ist kein Problem, was Sie versuchen zu erreichen? – ajreal

Antwort

4

Ok, da sie Techniken suchen, lassen Sie mich einige Liste ...

1. Sie keine Dateien lesen, sie streamen

Anstatt $data = file_get_contents($file) Aufruf, öffnen Sie es mit fopen und lesen Sie nur die Daten, die Sie zu diesem Zeitpunkt benötigen (fgets oder fgetcsv, usw.). Es ist eine Berührung langsamer, aber es wird viel weniger Speicher verwenden.

2. Upgrade auf 5.3.4

Wenn Sie noch auf PHP 5.2.x sind, werden Speicher stark durch ein Upgrade auf 5.3.x (neueste 5.3.4) konserviert werden. Es enthält einen Garbage Collector, der freigegebenen Speicher nach einer Weile bereinigen wird.

3. Sie nichts global im Umfang nutzen alle Informationen im globalen Bereich nicht speichern

Sie. Es wird nie bis zum Ende der Ausführung gereinigt, so dass es an und für sich ein Speicherleck sein könnte.

4. Sie laufen um nicht referenziert

PHP verwendet Copy-on-Recht. Das Umgehen von Referenzen erhöht nur die Wahrscheinlichkeit, dass unset nicht alle von ihnen bekommt (weil Sie unset eine der Referenzen vergessen haben). Übergeben Sie stattdessen einfach die tatsächlichen Variablen.

5. Profil der Code

Code Profil. Fügen Sie am Anfang und am Ende jedes Funktionsaufrufs Debug-Hooks hinzu und protokollieren Sie sie dann, indem Sie die Speicherbelegung am Eingang und am Ende jeder Funktion beobachten. Nehmen Sie die Diffs von diesen und Sie werden wissen, wie viel Speicher von jeder Funktion verwendet wird. Nehmen Sie die größten Täter (die, die viel genannt werden, oder benutzen Sie viel Gedächtnis) und säubern Sie sie ... (niedrigste hängende Frucht).

6.Verwenden Sie eine andere Sprache

Während Sie dies mit PHP tun können (ich habe und oft), ist es nicht das beste Werkzeug für den Job. Andere Sprachen wurden für dieses genaue Problem entworfen, also warum nicht verwenden, einen von ihnen (Python oder Perl zum Beispiel) ...

7. Verwenden Sie Scratch-Dateien

Wenn Sie den Überblick über eine Menge zu halten brauchen von Daten, speichern Sie nicht alle im Speicher die ganze Zeit. Erstellen Sie Arbeitsdateien (temporäre Dateien), um die Daten zu speichern, wenn Sie sie nicht explizit verwenden. Laden Sie die Datei nur, wenn Sie diese spezifischen Daten verwenden, und speichern Sie sie erneut und entfernen Sie die Variablen.

8. Nur Extremfälle: keine großen Arrays verwenden!

Wenn Sie eine große Anzahl von ganzen Zahlen (oder anderen einfachen Datentypen) verfolgen müssen, speichern Sie sie nicht in einem Array! Die zval (interne Datenstruktur) hat einen gewissen Overhead. Wenn Sie tatsächlich eine große Anzahl von ganzen Zahlen (Hunderttausende oder Millionen) speichern müssen, verwenden Sie eine Zeichenfolge. Bei einem 1-Byte-Int wird ord($numbers[$n]) den Wert des $n Indexes erhalten, und $numbers[$n] = chr($value); wird es setzen. Für Multibyte-Ints müssten Sie $n * $b tun, um den Anfang der Sequenz zu erhalten, wobei $b die Anzahl der Bytes ist. Ich betone, dass dies nur in dem extremen Fall verwendet werden sollte, in dem Sie eine TON von Daten speichern müssen. In Wirklichkeit besser wäre dies durch eine Scratch-Datei oder eine tatsächliche Datenbank (temporäre Tabelle wahrscheinlich) bedient, so dass es keine gute Idee ... sein kann

Good Luck ...

+0

Wow, danke für die gründliche Beratung. Ich mache bereits 1, 2 und 8, aber keine der anderen. Was ich tatsächlich tun könnte, ist eine zweistufige Sache: 1) zerschneide die Dateien in kleinere Stücke als temporäre Abhilfe und 2) schreibe das Importskript wie Perl, Python oder wie du es vorschreibst. Ich wusste nicht, dass bestimmte Sprachen speziell für das Import/ETL-Problem entwickelt wurden. –

+0

@Jason: Nun, es ist nicht so, dass sie speziell für den Import geschrieben wurden, sie wurden gebaut, um generisch zu laufen. PHP wurde erstellt, um Webseiten zu erstellen. Dann realisierten die Leute, dass es andere Dinge tun konnte und Unterstützung wurde als Nebeneffekt hinzugefügt. Der primäre Anwendungsfall von PHP ist nicht lange laufende Anwendungen (daher der Mangel an Speicher-Tools). Aber andere wie Perl oder Python wurden in diesem Bereich viel mehr verwendet.Aber wie viel Speicher reden wir, dass Sie zu viel verwenden? Ein paar kb von einer Funktion sollten keinen Schaden verursachen. Sie sollten in der Lage sein, es ziemlich gut zu profilieren, wenn Sie eine Tonne verwenden ... – ircmaxell

+0

Ich erreiche 100% Speicherverbrauch (oder nah daran) Teilweise durch meine Importe auf einer Maschine mit 4GB RAM. –

0

Können Sie das Skript mehrmals ausführen und nur jedes Mal eine kleine Anzahl von Dateien verarbeiten? Wenn Sie Summen oder Ähnliches sammeln, können Sie diese in einer Datei oder Memcached speichern, so dass Sie immer noch eine laufende Summe behalten können.

+0

Ich habe eine ähnliche Lösung in Betracht gezogen. Mein Skript verarbeitet bereits jeweils nur eine Datei. Es ist nur so, dass die meisten dieser CSV-Dateien etwa 50.000 Zeilen umfassen. Ich könnte jede Datei in mehrere Dateien aufteilen. Das "behebt" das Problem, aber das fühlt sich an wie eine lahme Lösung. –