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? :(

0voto

Ich habe mich mit diesem Problem schon eine Weile intensiv beschäftigt. Und ja, die bessere Lösung ist es, immer nur einen Teil der Datei einzulesen, diesen zu analysieren, zu validieren, zu filtern, zu exportieren und dann den nächsten Teil der Datei zu lesen. Ich würde zustimmen, dass dies wahrscheinlich keine Lösung für php ist, obwohl man es wahrscheinlich in php machen kann. Solange Sie eine Suchfunktion haben, mit der Sie das Lesen an einer bestimmten Stelle in der Datei beginnen können. Sie haben Recht, dass es einen höheren Grad an Komplexität mit sich bringt, aber der kleine Mehraufwand lohnt sich. Wenn Ihre Daten rein sind, d. h. korrekt abgegrenzt, stringqualifiziert, frei von Zeilenumbrüchen usw., dann können Sie sie auf jeden Fall in eine SQL-Datenbank hochladen. Andernfalls müssen Sie wissen, wo, wann und warum Fehler auftreten, und in der Lage sein, diese zu behandeln.

0voto

heavyrick Punkte 376

Ich arbeite mit etwas Ähnlichem.

Die csv-Datei, mit der ich arbeite, enthält portugiesische Daten (dd/mm/yyyy), die ich in mysql yyyy-mm-dd konvertieren muss. Portugiesisches Geld: R$ 1.000,15, die in mysql dezimal 1000,15 umgewandelt werden müssen. Trimmen Sie die möglichen Leerzeichen und fügen Sie schließlich Schrägstriche hinzu.

Es gibt 25 Variablen, die vor dem Einfügen zu behandeln sind.

Wenn ich jeden $notafiscal-Wert überprüfe (select in die Tabelle, um zu sehen, ob es existiert und zu aktualisieren), verarbeitet die php rund 60k Zeilen. Aber wenn ich es nicht überprüfe, verarbeitet php mehr als 1 Million Zeilen.

Der Server arbeitet mit einem Arbeitsspeicher von 4 GB - Scripting Localhosting (Arbeitsspeicher von 2 GB), er verarbeitet die halben Zeilen in beiden Fällen.

mysqli_query($db,"SET AUTOCOMMIT=0");
mysqli_query($db, "BEGIN");
mysqli_query($db, "SET FOREIGN_KEY_CHECKS = 0");
fgets($handle); //ignore the header line of csv file

while (($data = fgetcsv($handle, 100000, ';')) !== FALSE):
 //if $notafiscal lower than 1, ignore the record
 $notafiscal = $data[0];  
 if ($notafiscal < 1):
  continue;
 else:
  $serie = trim($data[1]); 
  $data_emissao = converteDataBR($data[2]);
  $cond_pagamento = trim(addslashes($data[3]));
  //...
  $valor_total = trim(moeda($data[24]));
  //check if the $notafiscal already exist, if so, update, else, insert into table
  $query = "SELECT * FROM venda WHERE notafiscal = ". $notafiscal ;
  $rs = mysqli_query($db, $query);
  if (mysqli_num_rows($rs) > 0):
    //UPDATE TABLE
  else:
    //INSERT INTO TABLE
  endif;
endwhile;

mysqli_query($db,"COMMIT");
mysqli_query($db,"SET AUTOCOMMIT=1");
mysqli_query($db,"SET FOREIGN_KEY_CHECKS = 1");
mysqli_close($db);

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