7 Stimmen

Bewährte Verfahren für die Iteration über MASSIVE CSV-Dateien in PHP

Okay, ich werde versuchen, mich kurz zu fassen und auf den Punkt zu kommen.

Wir führen umfangreiche GeoIP-Updates in unserem System durch, indem wir eine MASSIVE CSV-Datei in unser PHP-basiertes CMS hochladen. Diese Datei enthält in der Regel mehr als 100.000 Datensätze mit IP-Adressinformationen. Ein einfacher Import dieser Daten ist überhaupt kein Problem, aber wir müssen sie mit unseren aktuellen regionalen IP-Adresszuordnungen abgleichen.

Das bedeutet, dass wir die Daten validieren, überlappende IP-Adressen vergleichen und aufteilen müssen usw.. Und diese Prüfungen müssen für jeden einzelnen Datensatz durchgeführt werden.

Darüber hinaus habe ich gerade eine Lösung für die Feldzuordnung entwickelt, die es anderen Anbietern ermöglicht, ihre GeoIP-Aktualisierungen in verschiedenen Formaten zu implementieren. Dies geschieht durch die Anwendung von Regeln auf IPs-Datensätze innerhalb der CSV-Aktualisierung.

Eine Regel könnte zum Beispiel so aussehen:

wenn 'countryName' == 'Australia' dann an den 'Australian IP Pool' senden

Es kann mehrere Regeln geben, die ausgeführt werden müssen, und jeder IP-Eintrag muss sie alle anwenden. Bei 100k Datensätzen, die anhand von 10 Regeln zu prüfen sind, würde dies 1 Million Iterationen bedeuten - kein Spaß.

Wir haben festgestellt, dass die Verarbeitung von 2 Regeln für 100k Datensätze bis zu 10 Minuten dauert. Ich bin mir des Engpasses voll bewusst, der in der schieren Menge der Iterationen besteht, die für einen erfolgreichen Import erforderlich sind; ich bin mir nur nicht voll bewusst, welche anderen Optionen wir haben, um die Dinge etwas zu beschleunigen.

Jemand hat empfohlen, die Datei serverseitig in Teile aufzuteilen. Ich glaube nicht, dass dies eine praktikable Lösung ist, da es ein bereits komplexes System um eine weitere Ebene erweitert. Die Datei müsste geöffnet, geparst und aufgeteilt werden. Dann müsste das Skript auch noch über die Chunks iterieren.

Die Frage ist also, in Anbetracht dessen, was ich gerade geschrieben habe, was wäre die BESTE Methode, um diesen Prozess ein wenig zu beschleunigen? Upgrading der Server-Hardware NUR für dieses Tool ist leider keine Option, aber sie sind ziemlich High-End-Boxen mit zu beginnen.

Nicht so kurz, wie ich dachte, aber ja. Halbsätze? :(

13voto

Some Canuck Punkte 826

Führen Sie einen BULK IMPORT in eine Datenbank durch (ich verwende SQL Server). Der BULK IMPORT dauert buchstäblich nur Sekunden, und 100.000 Datensätze sind für eine Datenbank, die Geschäftsregeln verarbeitet, ein Klacks. Ich führe regelmäßig ähnliche Datenabfragen für eine Tabelle mit über 4 Millionen Zeilen durch, und es dauert nicht einmal die von Ihnen genannten 10 Minuten.

EDIT: Ich sollte darauf hinweisen, ja, ich empfehle nicht PHP für diese. Sie haben es mit Rohdaten zu tun, verwenden Sie eine DATENBANK :P

0 Stimmen

Wie schade - ich habe nie eine "akzeptierte Antwort" darauf erhalten.

1voto

Will Hartung Punkte 110997

Der einfache Schlüssel dazu ist, so viel Arbeit wie möglich aus der inneren Schleife herauszuhalten.

Einfach ausgedrückt: Alles, was Sie in der inneren Schleife tun, wird "100K mal" ausgeführt, also ist es am besten, nichts zu tun (aber sicherlich nicht praktisch), also ist es am besten, so wenig wie möglich zu tun.

Wenn Sie z.B. über den nötigen Speicher verfügen und es für die Anwendung praktisch ist, verschieben Sie die "Ausgabe" bis nach der Hauptverarbeitung. Zwischenspeichern Sie auch alle Eingabedaten, wenn dies sinnvoll ist. Dies funktioniert am besten bei zusammengefassten Daten oder gelegentlichen Daten.

Idealerweise sollten Sie während der Hauptverarbeitung so wenig E/A wie möglich durchführen, außer beim Lesen der CSV-Datei.

Bietet PHP Zugriff auf die Unix mmap-Funktion, die normalerweise der schnellste Weg ist, um Dateien zu lesen, insbesondere große Dateien.

Eine weitere Überlegung ist, die Beilagen zu dosieren. Es ist zum Beispiel einfach, Ihre INSERT-Anweisungen als einfache Strings zu erstellen und sie in Blöcken von 10, 50 oder 100 Zeilen an den Server zu senden. Die meisten Datenbanken haben eine harte Grenze für die Größe der SQL-Anweisung (z.B. 64K oder so), die Sie im Hinterkopf behalten müssen. Auf diese Weise können Sie die Anzahl der Übertragungen an die DB drastisch reduzieren.

Wenn Sie Primärschlüssel durch einfache Inkremente erstellen, tun Sie dies in Massen (Blöcke von 1000, 10000, was auch immer). Dies ist ein weiterer Punkt, den Sie aus Ihrer inneren Schleife entfernen können.

Und natürlich sollten Sie alle Regeln für jede Zeile auf einmal verarbeiten und nicht die Datensätze für jede Regel durchlaufen.

1voto

Gary Richardson Punkte 15543

100k Datensätze sind keine große Zahl. 10 Minuten sind keine schlechte Zeit für die Bearbeitung eines Auftrags durch einen einzelnen Thread. Die Menge an Roharbeit, die in einer geraden Linie erledigt werden muss, beträgt wahrscheinlich etwa 10 Minuten, unabhängig davon, ob Sie PHP oder C verwenden. Wenn Sie es schneller haben wollen, brauchen Sie eine komplexere Lösung als eine while-Schleife.

Ich würde folgendermaßen vorgehen:

  1. Verwenden Sie eine Map/Reduce-Lösung, um den Prozess parallel laufen zu lassen. Hadoop ist wahrscheinlich ein Overkill. Pig Latin könnte die Aufgabe erfüllen. Sie wollen eigentlich nur den Map-Teil des Map/Rece-Problems. D.h. Sie forken einen Teil der Datei ab, der von einem Unterprozess verarbeitet werden soll. Ihr Reducer ist wahrscheinlich cat . Eine einfache Version könnte darin bestehen, dass PHP Prozesse für jeden 10K-Datensatz abspaltet, auf die Kinder wartet und dann deren Ausgabe wieder zusammenfügt.
  2. Verwenden Sie ein Warteschlangen-/Gitterverarbeitungsmodell. Stellen Sie Teile der Datei in eine Warteschlange, und lassen Sie dann einen Cluster von Maschinen einchecken, die Aufträge abholen und die Daten irgendwo hinschicken. Dies ist dem Map/Rece-Modell sehr ähnlich, nur mit anderen Technologien. Außerdem können Sie das Grid durch Hinzufügen weiterer Maschinen skalieren.
  3. Wenn Sie Ihre Logik als SQL schreiben können, tun Sie es in einer Datenbank. Ich würde dies vermeiden, weil die meisten Webprogrammierer nicht mit SQL auf diesem Niveau arbeiten können. Außerdem ist SQL für Dinge wie RBL-Prüfungen oder ARIN-Abfragen nur bedingt geeignet.

0voto

Sie können versuchen, den CSV-Import über die PHP-Befehlszeile auszuführen. Dies liefert im Allgemeinen schnellere Ergebnisse.

0voto

Alix Axel Punkte 146320

Wenn Sie PHP verwenden, um diese Aufgabe zu erledigen, schalten Sie das Parsing auf Python um, da es in diesem Bereich VIEL schneller ist als PHP. Dieser Austausch sollte den Prozess um 75 % oder sogar mehr beschleunigen.

Wenn Sie MySQL verwenden, können Sie auch den Operator LOAD DATA INFILE verwenden. Ich bin mir jedoch nicht sicher, ob Sie die Daten vor dem Einfügen in die Datenbank überprüfen müssen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X