While Schleife - Zu bestimmter Stelle springen
Moin,
ich habe ein Import, mit welcher ich 100t Datensätze aus einer CSV in eine DB importiere.
Das Ganze läuft über ein Script, welches sich wegen Servertimeout immer wieder selbst aufruft.
Es gibt ein Step Wert und ein Offset Wert.
Step=500
Offset0
D.h. Gestartet wird mit dem Datensatz 0 und es werden 500 abgearbeitet. Dann wird das Script erneut aufgerufen mit dem Step=500 und dem Offset= 500.
Jetzt werden die ersten 500 Datensätze übersprungen und dann geht es ab 500 weiter mit dem Import. usw.
Jetzt meine Frage. Je länge, das Script läuft, um so langsamer wird es bei den letzte Datensätzen. Da ja z.B bei dem Offset 50000, davor in der Schleife erst mal 49999 übersprungen werden, bis dann ab 50000 die nächsten 500 importiert werden.
Es muss doch möglich sein, direkt den Einstiegspunkt mitzugeben. So dass nicht erst die bereits importierten Datensätze übersprungen werden müssen.
ich hoffe ihr versteht wie ich das meine ;)
Besten Dank
Gruß ottscho
ich habe ein Import, mit welcher ich 100t Datensätze aus einer CSV in eine DB importiere.
Das Ganze läuft über ein Script, welches sich wegen Servertimeout immer wieder selbst aufruft.
Es gibt ein Step Wert und ein Offset Wert.
Step=500
Offset0
D.h. Gestartet wird mit dem Datensatz 0 und es werden 500 abgearbeitet. Dann wird das Script erneut aufgerufen mit dem Step=500 und dem Offset= 500.
Jetzt werden die ersten 500 Datensätze übersprungen und dann geht es ab 500 weiter mit dem Import. usw.
$row = 1; // Anzahl der Arrays
$handle = fopen ("upload/artikel.csv","r"); // Datei zum Lesen �ffnen
// Die erste Zeile mit den Spaltennamen auslesen
$data = fgetcsv ($handle, 100000, ";");
if(is_array($data)) {
foreach($data AS $cellNr => $cellName) {
$cellNamesArray[$cellNr] = $cellName;
}
}
$return=false;
while ( ($data = fgetcsv ($handle, 100000, ";")) !== FALSE ) { // Daten werden aus der Datei
$num = count ($data); // Felder im Array $data
//var_dump('row: '.$row);
//var_dump('offset: '.$offset);
//var_dump('step: '.$step);
if ($row <= $offset) {
$row++; // Anzahl der Arrays wird
continue;
}
if ($row > $offset+$step) {
$return=true;
break;
}
$row++; // Anzahl der Arrays wird
$count = 0;
for ($c=0; $c < $num; $c++) { // FOR-Schleife, um Felder des Arrays auszugeben
$feld[$cellNamesArray[$c]] = $data[$c];
$count++;
}
//**********************//
//*** IMPORT Routine ***//
//**********************//
}
Jetzt meine Frage. Je länge, das Script läuft, um so langsamer wird es bei den letzte Datensätzen. Da ja z.B bei dem Offset 50000, davor in der Schleife erst mal 49999 übersprungen werden, bis dann ab 50000 die nächsten 500 importiert werden.
Es muss doch möglich sein, direkt den Einstiegspunkt mitzugeben. So dass nicht erst die bereits importierten Datensätze übersprungen werden müssen.
ich hoffe ihr versteht wie ich das meine ;)
Besten Dank
Gruß ottscho
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 203001
Url: https://administrator.de/contentid/203001
Ausgedruckt am: 25.11.2024 um 14:11 Uhr
15 Kommentare
Neuester Kommentar
Hallo
ich habe ein Import, mit welcher ich 100t Datensätze aus einer CSV in eine DB importiere.
Das Ganze läuft über ein Script, welches sich wegen Servertimeout immer wieder selbst aufruft.
Es gibt ein Step Wert und ein Offset Wert.
Step=500
Offset0
D.h. Gestartet wird mit dem Datensatz 0 und es werden 500 abgearbeitet. Dann wird das Script erneut aufgerufen mit dem Step=500
und dem Offset= 500.
Jetzt werden die ersten 500 Datensätze übersprungen und dann geht es ab 500 weiter mit dem Import. usw.
Das Ganze läuft über ein Script, welches sich wegen Servertimeout immer wieder selbst aufruft.
Es gibt ein Step Wert und ein Offset Wert.
Step=500
Offset0
D.h. Gestartet wird mit dem Datensatz 0 und es werden 500 abgearbeitet. Dann wird das Script erneut aufgerufen mit dem Step=500
und dem Offset= 500.
Jetzt werden die ersten 500 Datensätze übersprungen und dann geht es ab 500 weiter mit dem Import. usw.
Idee ist okay.. aber:
Jetzt meine Frage. Je länge, das Script läuft, um so langsamer wird es bei den letzte Datensätzen. Da ja z.B bei
dem Offset 50000, davor in der Schleife erst mal 49999 übersprungen werden, bis dann ab 50000 die nächsten 500
importiert werden.
dem Offset 50000, davor in der Schleife erst mal 49999 übersprungen werden, bis dann ab 50000 die nächsten 500
importiert werden.
Und hier hast du den Grund dafür. Das Teil muss ja so jedes Mal alle vorherigen Datensätze "überspringen".
if ($row <= $offset) {
$row++; // Anzahl der Arrays wird
continue;
}
if ($row > $offset+$step) {
$return=true;
break;
}
$row++; // Anzahl der Arrays wird
Du hättest jetzt folgende Möglichkeiten, die Ausführungszeit hoch zu setzen - was ich nicht besonders schön finde.
Leider weiß ich nicht, wie du genau den Import löst.
Läuft das per MySQL und INSERT's?
Wenn ja, dann schau dir mal folgende Funktion an:
http://dev.mysql.com/doc/refman/5.1/de/load-data.html
Ah okay,
vielleicht wäre noch folgendes möglich:
Du durchläufst die 5000 INSERTs und nimmst dann die ersten 5000 Einträge aus der Datei raus.
Somit wird die Datei kleiner und du kannst dir die Durchläufe der Schleife sparen.
vielleicht wäre noch folgendes möglich:
Du durchläufst die 5000 INSERTs und nimmst dann die ersten 5000 Einträge aus der Datei raus.
Somit wird die Datei kleiner und du kannst dir die Durchläufe der Schleife sparen.
Das Ganze läuft über ein Script, welches sich wegen Servertimeout immer wieder selbst aufruft
... umgehen kann man dies in dem man das php Skript direkt auf der Konsole aufruft. Oder man macht den Shell Aufruf aus deinem Web-PHP Skript$return = `nohup php mein_scipt.php >stdout.log 2>stderr.log </dev/null & echo $!`;
Guten Morgen,
ich hab von PHP keine Ahnung, soviel vorweg
ich würde, wenn ich sowas mit Perl mache, einen etwas anderen Ansatz wählen (oder zumindest ausprobieren).
Eine Schleife 50000 mal zu durchlaufen kostet Zeit. Stattdessen würde ich die letzte verarbeitet Zeile (oder eine Teilzeichenkette daraus) in einer Datei speichern. Wenn das Programm wieder startet, dann öffnet es die csv-Datei und liest die Zeichenkette ein. Wenn nun in der geöffneten Datei nach der Zeichenkette gesucht wird (PHP wird ja eine Variante von grep haben), dann geht das höchstwahrscheinlich erheblich schneller als Schleifendurchlauf. Die Geschwindigkeit hängt auch davon ab, welchen Teilstring man verwendet (sollte so verschieden als möglich von anderen Strings sein).
Gerade ausprobiert mit Perl, es bringt nichts.
Markus
Zitat von @ottscho:
...
Jetzt meine Frage. Je länge, das Script läuft, um so langsamer wird es bei den letzte Datensätzen. Da ja z.B bei
dem Offset 50000, davor in der Schleife erst mal 49999 übersprungen werden, bis dann ab 50000 die nächsten 500
importiert werden.
Es muss doch möglich sein, direkt den Einstiegspunkt mitzugeben. So dass nicht erst die bereits importierten Datensätze
...
Jetzt meine Frage. Je länge, das Script läuft, um so langsamer wird es bei den letzte Datensätzen. Da ja z.B bei
dem Offset 50000, davor in der Schleife erst mal 49999 übersprungen werden, bis dann ab 50000 die nächsten 500
importiert werden.
Es muss doch möglich sein, direkt den Einstiegspunkt mitzugeben. So dass nicht erst die bereits importierten Datensätze
ich würde, wenn ich sowas mit Perl mache, einen etwas anderen Ansatz wählen (oder zumindest ausprobieren).
Eine Schleife 50000 mal zu durchlaufen kostet Zeit. Stattdessen würde ich die letzte verarbeitet Zeile (oder eine Teilzeichenkette daraus) in einer Datei speichern. Wenn das Programm wieder startet, dann öffnet es die csv-Datei und liest die Zeichenkette ein. Wenn nun in der geöffneten Datei nach der Zeichenkette gesucht wird (PHP wird ja eine Variante von grep haben), dann geht das höchstwahrscheinlich erheblich schneller als Schleifendurchlauf. Die Geschwindigkeit hängt auch davon ab, welchen Teilstring man verwendet (sollte so verschieden als möglich von anderen Strings sein).
Gerade ausprobiert mit Perl, es bringt nichts.
Markus
Moin,
was ich mich frage ist, warum man eine Interpretersprache dafür nimmt? Ich würde da einfach ein C-Binary daraus machen (notfall smit PHC & gcc) und dann das aufrufen.
Was spricht eigentlich dagegen die Eingangsdatei einfach in Häppchen zu je 500 Datensätzen (als Einzeldateien) aufzuteilen und dann den Import auf jeweils diese Häppchen loszulassen?
lks
was ich mich frage ist, warum man eine Interpretersprache dafür nimmt? Ich würde da einfach ein C-Binary daraus machen (notfall smit PHC & gcc) und dann das aufrufen.
Was spricht eigentlich dagegen die Eingangsdatei einfach in Häppchen zu je 500 Datensätzen (als Einzeldateien) aufzuteilen und dann den Import auf jeweils diese Häppchen loszulassen?
lks
Zitat von @ottscho:
moin,
>was ich mich frage ist, warum man eine Interpretersprache dafür nimmt? Ich würde da einfach ein C-Binary daraus
>machen (notfall smit PHC & gcc) und dann das aufrufen.
Naja, die Shopschnittstelle für den Import ist eine API in PHP.
moin,
>was ich mich frage ist, warum man eine Interpretersprache dafür nimmt? Ich würde da einfach ein C-Binary daraus
>machen (notfall smit PHC & gcc) und dann das aufrufen.
Naja, die Shopschnittstelle für den Import ist eine API in PHP.
Da gibt es trotzdem Möglichkeiten, wie gesagt zur Not mal PCH ausprobieren.
>Was spricht eigentlich dagegen die Eingangsdatei einfach in Häppchen zu je 500 Datensätzen (als Einzeldateien)
>aufzuteilen und dann den Import auf jeweils diese Häppchen loszulassen?
nichts. so läuft im moment der erste ansatz. wir machen 10t csv Splittdateien. Sprich die Eingangsdatei mit den 120t Artikel
wird in 12 Einzeldateien gesplittet.
Dies sollte schon sehr viel Zeit sparen.
Sind die CSV-Datensätze "einzeilig", d.h. pro Datensatz eine zeile oder sind da zeilenumbrüche drin? Wenn sie einzeilig sind, könnte man durch ein einfaches
cat csv-datei | head -n start+500 | tail -n 500 >temp.csv
eine temporäre Datei erzeugen, die genau die gerade zu bearbeitenden Sätze enthält und schnell genug sein sollte
lks
Zitat von @Lochkartenstanzer:
...
eine temporäre Datei erzeugen, die genau die gerade zu bearbeitenden Sätze enthält und schnell genug sein sollte
das sieht gut aus, ist jedenfalls dreimal so schnell wie mein Versuch mit Perl ...
> cat csv-datei | head -n start+500 | tail -n 500 >temp.csv
>
eine temporäre Datei erzeugen, die genau die gerade zu bearbeitenden Sätze enthält und schnell genug sein sollte
den Teil mit cat bis zum ersten '|' kann man auch weglassen und direkt die Ausgabe von head nach tail pipen.
Spricht etwas dagegen, keine temporäre Datei anzulegen, sondern die 500 Zeilen direkt an das PHP-Programm zu übergeben? Dazu bräuchte man ein kleines Shellskript welches dann auch das PHP-Programm aufruft.
Markus
ich wollte eigentlich genauer sagen: man reduziert damit die Anzahl der Dateiöffnungen um die Hälfte. Außerdem werden diese nur noch von head gemacht was viel schneller sein dürfte als mit PHP.
Interessantes Thema übrigens, mich würde interessieren, wenn es fertig ist, wie es mit der tatsächlichen Zeitersparnis gegenüber Eurem ersten Ansatz aussieht.
Markus
Interessantes Thema übrigens, mich würde interessieren, wenn es fertig ist, wie es mit der tatsächlichen Zeitersparnis gegenüber Eurem ersten Ansatz aussieht.
Markus
Zitat von @64748:
> Zitat von @Lochkartenstanzer:
> ...
>
den Teil mit cat bis zum ersten '|' kann man auch weglassen und direkt die Ausgabe von head nach tail pipen.
> Zitat von @Lochkartenstanzer:
> ...
>
> > cat csv-datei | head -n start+500 | tail -n 500 >temp.csv
> >
Normal hätte ich das als
cat csv-datei | head -n start+500 | tail -n 500 | tee temp.cs
Ich benutze gern cat, weil ich solche Ketten meist "interaktiv" aufbaue und so einfach ein weiteres "|" mit Befehlen zwischenreinsetzen kann, ohne die schon vorhanden Kette groß verändern zu müssen. iIn der endgültigen Variante kann man natürlich dann verschiedene Pipe-Stufen "wegkürzen".
lks
Hi ottscho,
sofern nur die Importroutine das Timeout-Problem verursacht:
- Definiere einen Zähler für alle Zeilen
- Definiere einen Wert für die Zeilen, die pro Import importiert werden sollen
Wenn der Zähler_für_alle_Zeilen modulo_dividiert Zeilen_pro_Import 0 ergibt . . .
- Importroutine aufrufen
- Zähler_für_alle_Zeilen auf 0 setzen
Anschließend werden die letzten Zeilen eingelesen(importiert).
Beispiel:
Gruß
Günni
sofern nur die Importroutine das Timeout-Problem verursacht:
- Definiere einen Zähler für alle Zeilen
- Definiere einen Wert für die Zeilen, die pro Import importiert werden sollen
Wenn der Zähler_für_alle_Zeilen modulo_dividiert Zeilen_pro_Import 0 ergibt . . .
- Importroutine aufrufen
- Zähler_für_alle_Zeilen auf 0 setzen
Anschließend werden die letzten Zeilen eingelesen(importiert).
Beispiel:
<?php
$lines=0;
$max_lines=500;
$f=fopen("doku.txt","r");
while($line=fgets($f)){
$line_arr[$lines]=$line;
$lines++;
if($lines%$max_lines==0){
echo "// Importroutine: ";
echo $lines.' Zeilen werden importiert.<br>';
$lines=0;
}
}
/*
* Restliche Zeilen importieren
*/
while($line=fgets($f)){
$line_arr[$lines]=$line;
$lines++;
}
if($lines>0){
echo "// Importroutine: ";
echo $lines.' Zeilen werden importiert.<br>';
}
fclose($f);
?>
Gruß
Günni