MySQL / PHP zu langsam

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
comes
Beiträge: 2702
Registriert: 11.03.2005 07:33:30
Wohnort: /dev/null
Kontaktdaten:

MySQL / PHP zu langsam

Beitrag von comes » 08.03.2006 19:02:05

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
grüße, comes

Faschismus ist keine Meinung, sondern ein Verbrechen!
http://sourcewars.de

Benutzeravatar
seep
Beiträge: 544
Registriert: 31.10.2004 14:21:08
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: HSK

Beitrag von seep » 09.03.2006 08:03:15

Das ganze Script muß einfach langsam laufen! :wink:

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.
Nenne wir das PHP-Script einfach mal: csv2sql.php. Mache aus jedem "$query=" eine "print" mit einem \n am Ende und schmeiße den ganzen Datenbank-Krempel raus, den HTML-Code natürlich auch.

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
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.

comes
Beiträge: 2702
Registriert: 11.03.2005 07:33:30
Wohnort: /dev/null
Kontaktdaten:

Beitrag von comes » 09.03.2006 16:53:00

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
grüße, comes

Faschismus ist keine Meinung, sondern ein Verbrechen!
http://sourcewars.de

Benutzeravatar
seep
Beiträge: 544
Registriert: 31.10.2004 14:21:08
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: HSK

Beitrag von seep » 09.03.2006 17:17:51

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
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.

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);
dann kommt nach dem sort folgendes heraus:

Code: Alles auswählen

insert into foo ('bar', 42);
insert into foo ('hello world', 4711);

comes
Beiträge: 2702
Registriert: 11.03.2005 07:33:30
Wohnort: /dev/null
Kontaktdaten:

Beitrag von comes » 09.03.2006 17:20:26

soweit so gut.. nur kennt windows kein sort -u
grüße, comes

Faschismus ist keine Meinung, sondern ein Verbrechen!
http://sourcewars.de

Benutzeravatar
seep
Beiträge: 544
Registriert: 31.10.2004 14:21:08
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: HSK

Beitrag von seep » 09.03.2006 17:37:09

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?

comes
Beiträge: 2702
Registriert: 11.03.2005 07:33:30
Wohnort: /dev/null
Kontaktdaten:

Beitrag von comes » 09.03.2006 17:40:47

naja... ein wenig weiß ich was du meinst, aber so ganz klar ist es mir noch nicht
grüße, comes

Faschismus ist keine Meinung, sondern ein Verbrechen!
http://sourcewars.de

Benutzeravatar
seep
Beiträge: 544
Registriert: 31.10.2004 14:21:08
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: HSK

Beitrag von seep » 09.03.2006 17:53:38

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:

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 ...
}
Dank der assoziativen Hashes ist es ein leichtes, auch komplexere Strukturen mal eben zu cachen und dann wieder sauber wegzuschreiben.

comes
Beiträge: 2702
Registriert: 11.03.2005 07:33:30
Wohnort: /dev/null
Kontaktdaten:

Beitrag von comes » 09.03.2006 19:40:56

das ist gut! das ist sehr gut!

das werd ich mal ausprobieren.. danke dir:D

wenn das klappt, hast was gut
grüße, comes

Faschismus ist keine Meinung, sondern ein Verbrechen!
http://sourcewars.de

Antworten