MySQL / PHP zu langsam
MySQL / PHP zu langsam
Hallihallo,
ich hab da ein kleines anliegen. ich hab den nen skript geschrieben, der mir aus ner excel tabelle, unter auflösung aller schlüssel, die daten nach mysql schreibt. das ding geht auch sehr gut und ist genau das was ich willl, aber viiiiiel zu langsam
auf meiner maschine brauch der 3 minuten um 6700 Datensätze zu schreiben. auf Arbeit auf der maschine satte 15 minuten. das kann nicht sein.
Konstruktive vorschläge sind erwünscht!
http://nopaste.debianforum.de/2580
ich hab da ein kleines anliegen. ich hab den nen skript geschrieben, der mir aus ner excel tabelle, unter auflösung aller schlüssel, die daten nach mysql schreibt. das ding geht auch sehr gut und ist genau das was ich willl, aber viiiiiel zu langsam
auf meiner maschine brauch der 3 minuten um 6700 Datensätze zu schreiben. auf Arbeit auf der maschine satte 15 minuten. das kann nicht sein.
Konstruktive vorschläge sind erwünscht!
http://nopaste.debianforum.de/2580
- seep
- Beiträge: 544
- Registriert: 31.10.2004 14:21:08
- Lizenz eigener Beiträge: GNU Free Documentation License
- Wohnort: HSK
Das ganze Script muß einfach langsam laufen!
Du machst ja für jede der 6700 Zeilen dutzendfache Lookups, wenn ich richtig gezählt habe stecken in der while-Schleife 23 (dreiundzwanzig) select-Statements in denen Du schaust, ob z.B. eine ID für eine town schon vergeben ist oder nicht. Das macht schon mehr als 150.000 Abfragen auf die Datenbank, die Inserts noch gar nicht mitgezählt.
Ob eine ID schon vergeben ist oder nicht, das ist Aufgabe der Datenbank, dies festzustellen. Vergebe einen Primary Key, und dann meckert MySQL schon, wenn Town mit ID 42 noch einmal eingefügt werden soll. Etwas unsauber, aber auf jeden Fall schneller als ein eigener Test über PHP.
Willst Du das ganze elegant und schnell erledigen, gehe folgendermaßen vor:
Dann läßt sich das alles brav in einem Rutsch so erledigen:
Auf einem 1 GHz-Rechner sollte solch ein Bash-Einzeiler dann deutlich weniger als 30 Sekunden benötigen, aus dem Bauch heraus würde ich auf max. 10 Sekunden tippen.
Du machst ja für jede der 6700 Zeilen dutzendfache Lookups, wenn ich richtig gezählt habe stecken in der while-Schleife 23 (dreiundzwanzig) select-Statements in denen Du schaust, ob z.B. eine ID für eine town schon vergeben ist oder nicht. Das macht schon mehr als 150.000 Abfragen auf die Datenbank, die Inserts noch gar nicht mitgezählt.
Ob eine ID schon vergeben ist oder nicht, das ist Aufgabe der Datenbank, dies festzustellen. Vergebe einen Primary Key, und dann meckert MySQL schon, wenn Town mit ID 42 noch einmal eingefügt werden soll. Etwas unsauber, aber auf jeden Fall schneller als ein eigener Test über PHP.
Willst Du das ganze elegant und schnell erledigen, gehe folgendermaßen vor:
- Rufe das PHP-Script nicht mittels HTTP/Apache auf, sondern starte es per Commandline.
- Führe in der PHP-Seite keine MySQL-Zugriffe durch, sondern erzeuge eine SQL-Datei, in die Du alle SQL-Inserts schreibst bzw. schreibe auf die Standardausgabe.
- Lasse vor der Übergabe an mysql ein "sort -u" drüberlaufen, dadurch filterst Du doppelte inserts und entlastest MySQL.
Dann läßt sich das alles brav in einem Rutsch so erledigen:
Code: Alles auswählen
php csv2sql.php | sort -u | mysql -u user -p password database
- seep
- Beiträge: 544
- Registriert: 31.10.2004 14:21:08
- Lizenz eigener Beiträge: GNU Free Documentation License
- Wohnort: HSK
a) Auch unter Windows kann man php.exe auf der Kommandozeile in der DOS-Box ausführen. Und die "Shell" unter Windows kennt auch Eingabe/Ausgabe-Umlenkung.comes hat geschrieben:gute idee, hatte ich auch schon, tuts aber nicht
a) ist der server ein windowsserver
b) kann nicht die querys nach einer datei schreiben.. ich will ja eben redundanzen vermeidene.
nach deiner idee haben ich von bsp id 10-25 ein und den selben datensatz
b) die Redundanzen werden ja durch das "sort -u" (u steht für "unique" = "einzigartig") entfernt.
Wenn in der Datei dann folgendes steht...
Code: Alles auswählen
insert into foo ('bar', 42);
insert into foo ('bar', 42);
insert into foo ('bar', 42);
insert into foo ('hello world', 4711);
insert into foo ('bar', 42);
insert into foo ('hello world', 4711);
Code: Alles auswählen
insert into foo ('bar', 42);
insert into foo ('hello world', 4711);
- seep
- Beiträge: 544
- Registriert: 31.10.2004 14:21:08
- Lizenz eigener Beiträge: GNU Free Documentation License
- Wohnort: HSK
Uhps, ich bin natürlich davon ausgegangen, daß aber auch wirklich jeder Cygwin und die GNU-Textutils unter Windows installiert hat. Asche auf mein Haupt.
Ähm, ja, also entweder installierst Du Dir eben Cygwin, eine andere sort-Portierung für Windows, oder wir wechseln die Strategie: anstelle der Queries "wenn keine town mit der ID 42 vorhanden ist dann mache einen insert" vielleicht innerhalb der while-Schleife in Arrays die Daten im RAM halten, und nach dem Einlesen aller CSV-Zeilen dann in einem Rutsch aus den Arrays die inserts bauen und aufrufen. Müßte auch flotter gehen. Verstanden was ich meine?
Ähm, ja, also entweder installierst Du Dir eben Cygwin, eine andere sort-Portierung für Windows, oder wir wechseln die Strategie: anstelle der Queries "wenn keine town mit der ID 42 vorhanden ist dann mache einen insert" vielleicht innerhalb der while-Schleife in Arrays die Daten im RAM halten, und nach dem Einlesen aller CSV-Zeilen dann in einem Rutsch aus den Arrays die inserts bauen und aufrufen. Müßte auch flotter gehen. Verstanden was ich meine?
- seep
- Beiträge: 544
- Registriert: 31.10.2004 14:21:08
- Lizenz eigener Beiträge: GNU Free Documentation License
- Wohnort: HSK
Nehmen wir mal weiterhin das Beispiel der Tabelle 'town'.
Du liest nun in der while-Schleife die gesamte CSV-Datei aus. Nun steht in Spalte x die ID der Town. Momentan machst Du einen SQL-Lookup und sagst "gib mir alle Einträge mit der townid 42". Wenn MySQL keine Treffer findet, machst Du einen insert in die Tabelle. Bei 6700 Zeilen in der CSV-Datei machst Du also auch 6700 SQL-Lookups auf die Town-Tabelle.
Merke Dir innerhalb der while-Schleife einfach das, was Du am Ende in der Tabelle haben möchtest. Pseudo-Code:
Dank der assoziativen Hashes ist es ein leichtes, auch komplexere Strukturen mal eben zu cachen und dann wieder sauber wegzuschreiben.
Du liest nun in der while-Schleife die gesamte CSV-Datei aus. Nun steht in Spalte x die ID der Town. Momentan machst Du einen SQL-Lookup und sagst "gib mir alle Einträge mit der townid 42". Wenn MySQL keine Treffer findet, machst Du einen insert in die Tabelle. Bei 6700 Zeilen in der CSV-Datei machst Du also auch 6700 SQL-Lookups auf die Town-Tabelle.
Merke Dir innerhalb der while-Schleife einfach das, was Du am Ende in der Tabelle haben möchtest. Pseudo-Code:
Code: Alles auswählen
$townArray= array();
solange (lese nächste CSV-Zeile)
{
$town=[Wert von Spalte x];
wenn [$town noch nicht in $townArray definiert ist]
{
packe $town ins $townArray;
}
}
füralle Einträge von $townArray
{
insert into mysql ...
}