Tim: Große Menge von Datensätzen aus DB in Datei schreiben

Hallo,

ich habe ein kleines Performanceproblem und weiß nicht wie ich dem begegnen soll.

Ich möchte gern etwas über 15000 Datensätze aus einer MySQL DB in eine csv Datei schreiben. Mit einer geringen Anzahl an Datensätzen klappt das soweit auch ganz gut. Aber wenn ich alle auf einmal schreiben möchte, lädt die Seite ewig bzw. es kommt erst gar nicht zu einem Resultat.

Hier mal ein Codeschipsel:

ob_end_clean();  
set_time_limit(0);		  
		  
/* DB Suche gibt Array von Objekten zurück -> $results */  
			  
$csv = utf8_decode("A;B;C;" . "\n");  
		  
foreach($results as $object) {			  
  $csv .= utf8_decode($object->getA()) . ";" .  
          utf8_decode($object->getB()) . ";" .  
          utf8_decode($object->getC()) . ";" .  
          "\n";  
}  
		  
header("Pragma: public");  
header("Content-Type: application/ms-excel; utf-8");  
header("Content-Length: " . strlen($csv));  
header("Content-Disposition: attachment; filename='test.csv'");  
		  
echo $csv;  
  
die();

Hat wer eine Idee, wie ich das besser machen kann?

MFG
Timo

  1. Hat wer eine Idee, wie ich das besser machen kann?

    Du hast ein Datenbankproblem, löse es mit der Datenbank.

    In MySQL steht dir z.B. SELECT ... INTO OUTFILE zur Verfügung.

    1. In MySQL steht dir z.B. SELECT ... INTO OUTFILE zur Verfügung.

      Funktioniert nicht da 1&1 (dort liegt das Projekt) OUTFILE unterbindet

  2. Hi!

    ich habe ein kleines Performanceproblem und weiß nicht wie ich dem begegnen soll.

    Zunächst mit Messen, damit du den Erfolg oder Misserfolg von Maßnahmen mit einer weiteren Messung feststellen kannst. Du solltest nicht nur generell messen sondern auch Teilabschnitte (zum Beispiel mit microtime()). Und dann kannst du versuchen mit Maßnahmen eine Verbesserung herbeizuführen.

    Weiterhin gibt es keine Patentrezepte für alle Lebenslagen. Du musst also etwas probierfreudig sein.

    Ich möchte gern etwas über 15000 Datensätze aus einer MySQL DB in eine csv Datei schreiben. [...] Aber wenn ich alle auf einmal schreiben möchte, lädt die Seite ewig bzw. es kommt erst gar nicht zu einem Resultat.

    Wie groß ist ein Datensatz durchschnittlich, damit man sich mal ein Bild von der Datenmenge machen kann?

    /* DB Suche gibt Array von Objekten zurück -> $results */

    Das könnte schon ein Teil des Problems sein. Warum legst du erst ein Array an? Brauchst du das im weiteren Verlauf des Scripts noch mal? Dann wäre die PHP-Version interessant. Üblicherweise holt ein mysql_query() im Hintergrund bereits alle Datensätze ab, nicht erst beim Fetchen. Das muss es tun, sonst wissen Funktionen wie mysql_num_rows() nichts Gescheites zu berichten. Es gibt aber auch eine ungepufferte Variante, mysql_unbuffered_query(). Wenn du das verwendest, wird bei den Fetch-Funktionen wirklich gefetcht und nicht nur der Puffer abgefragt. Zudem war das Puffern in PHP-Versionen kleiner als 5.3 nicht gerade speicherverbrauchsoptimiert. Das Zeug lag intern teilweise doppelt rum.

    Wenn du mysqli verwendest gibt es ein vergleichbares Verhalten und man kann ebenfalls zwischen gepuffert und ungepuffert wählen.

    $csv = utf8_decode("A;B;C;" . "\n");

    Auch das UTF-8-Dekodieren verbraucht Zeit. Lass das doch das DBMS tun, indem du es gleich um die Lieferung von Latin1 (ISO-8859-1) bittest (siehe http://wiki.selfhtml.org/wiki/Themen:Zeichencodierung/MySQL. Wo das Umkodieren stattfindet, falls es notwendig ist, spielt keine Rolle, aber die vielen Funktionsaufrufe in PHP verbrauchen auch Zeit, die ein einzelner Umkodiervorgang im MySQL-Server nicht benötigt.

    Vermutlich sparst du aber mehr durch das Ändern des Puffer-Verhaltens und wenn du das Array weglässt. Aber immer nach jeden einzelnen Schritt messen, ob er wirklich was gebracht hat.

    foreach($results as $object) {
      $csv .= utf8_decode($object->getA()) . ";" .

    Und dann baust du hier erst in einer weiteren Aktion aus den in das Array kopierten Daten durch das Anfügen vieler kleiner Teile einen einzelnen großen String zusammen, um den dann auszugeben. Das verbraucht natürlich auch Ressourcen.

    Hat wer eine Idee, wie ich das besser machen kann?

    Zusammenfassend denke ich, dass ungepuffertes Fetchen und sofortige Ausgabe ohne die Konvertierung nicht zu unterbieten ist.

    Lo!

    1. Zusammenfassend denke ich, dass ungepuffertes Fetchen und sofortige Ausgabe ohne die Konvertierung nicht zu unterbieten ist.

      Wenn man das DBMS alles machen lässt, könnte es nochmal schneller gehen - PHP müsste dann lediglich per readfile() das vom DBMS erzeugte CSV-File lesen und ein paar Header davorknallen.

      1. Hi!

        Zusammenfassend denke ich, dass ungepuffertes Fetchen und sofortige Ausgabe ohne die Konvertierung nicht zu unterbieten ist.

        Wenn man das DBMS alles machen lässt, könnte es nochmal schneller gehen - PHP müsste dann lediglich per readfile() das vom DBMS erzeugte CSV-File lesen und ein paar Header davorknallen.

        Dazu braucht es einige Voraussetzungen, die bei einem Webhoster in der Regel nicht erfüllt sind. Wenn man alle Komponenten unter eigener Kontrolle hat, geht das natürlich, aber warum sollte man dann noch eine PHP-Lösung suchen? SELECT ... INTO OUTFILE nicht zu kennen wäre ein Grund, ein schwacher.

        • Man benötigt das MySQL-Privileg, um Dateien auf dem MySQL-Server anlegen zu können.
        • Es gibt keine Verbindung zwischen den DBMS-Usernamen und den vom Betriebssystem verwalteten und damit laufen sämtliche Dateizugriffe mit der Kennung des MySQL-Servers. Existierende Dateien lassen sich zwar mit SELECT ... INTO OUTFILE nicht überschreiben, aber mit LOAD DATA INFILE lässt sich alles erreichbare lesen. Wer sowas für Massenhostingzwecke zulässt, ...
        • MySQL-Server und Webapplikationen sind meist getrennte Maschinen. Für den Dateizugriff muss auf der MySQL-Server die gleiche Benutzerverwaltung wie auf dem Webserver vorgehalten werden[*]. Technisch nicht das Problem, doch oftmals bedient ein MySQL-Server mehrere Webserver. Der Dateizugriff setzt eine Server-Software auf dem MySQL-Server voraus, also NFS oder CIFS oder FTP oder HTTP. Sowas will man aber nicht wirklich auch noch auf der Maschine laufen haben.

        [*] Eigentlich reicht Leserecht für alle und alles, denn der MySQL-Server unterscheidet beim Dateianlegen ja auch nicht.

        Lo!

        1. Das ist der Unterschied zwischen uns beiden ;) ich hab sämtliches Zeug auf einem verwalteten Server in meiner Nähe und gehe prinzipiell davon aus, dass "alles erlaubt ist". Du gehst prinzipiell davon aus, dass man irgend ein kleines Eckchen bei einem Massenhoster hat und da prinzipiell mal "nichts darf" :)