For-Schleife wird erschreckend langsam
-
Hallo zusammen !
Ich schreibe gerade an einem Programm, welches aus einer alten MySQL-Datenbank ganze Tabellen ausliest und Sie in eine andere schreibt. Dazu benutze ich 2 selbst entwickelte Klassen und eine For-Schleife. Soweit klappt auch alles ganz wunderbar. Nur hat eine Tabelle z.B. 14.000 Einträge und die ersten 4.000 - 5.000 Spalten rast das Programm die Daten nur so durch und danach wird es immer langsamer. Mir graut es daran zu denken, dass eine Tabelle dabei ist, welche ca. 130.000 Datensätze beinhaltet.
Hier mal ein Beispiel :
int nIndex = 0; CMySQLQuery *Import = new CMySQLQuery(MySQL); //Belegnummern cout << "Importiere Belgenummern ...\n"; Import->Query("SELECT * FROM %s.BNUM ORDER BY ID", sDefactoDB.c_str()); for (nIndex = 0; nIndex != Import->GetResultCount(); nIndex++) { MySQL->Statement("INSERT INTO tbl_BNUM (ID, TEXT, BID, NUM, Format) \ VALUES ('%s', '%s', '%s', '%s', '%s')", Import->GetCharValue(nIndex, "ID"), Import->GetCharValue(nIndex, "TEXT"), Import->GetCharValue(nIndex, "BID"), Import->GetCharValue(nIndex, "NUM"), Import->GetCharValue(nIndex, "Format")); cout << nIndex +1 << "/" << Import->GetResultCount() << "\r"; if (MySQL->GetErrorNumber() != 0) { cout << "ERROR : " << MySQL->GetErrorDescription() << endl; return false; } }Das ganze Programm wird im Rahmen eines Softwareupdates benötigt, wobei sich einige Tabllen-/ bzw. Feldnamen geändert haben. Deswegen können die Tabellen nicht 1:1 übernommen werden.
Ich bin hier relativ ratlos und eventuell sieht einer von euch meinen Fehler
Viele Grüße
Christian
-
Musst du GetResultCount immer wieder neu aufrufen?
-
Nein, eigentlich nicht, aber dann wäre die Schleife doch allgemein langsam oder ? 1/3 der Daten gehen sehr schnell (ca. 3 Sek) danach wird es wirklich sehr langsam.
-
Es wäre möglich, dass dein Programm im Laufe der Zeit etwas an Priorität verliert, da es konstant sehr viel CPU-Zeit in anspruch nimmt. Ich weiß nicht was für ein Betriebssystem du verwendest, aber das erhöhen der Prozess-Priorität könnte da abhilfe schaffen.
-
Muss man da eventuell nach jedem Aufruf etwas freigeben? Das Statement? Schau mal ob dein Programm immer mehr Speicher benötigt. Sobalds ans Auslagern geht wirds extrem langsam.
MfG SideWinder
-
Was sagt denn der Profiler? Ist nicht vielleicht MySQL->Statement() immer langsamer?
Es muss doch aber auch was besseress geben um massenhaft Daten zu importieren als dieses SQL dafür ständig zu benutzen.
-
Also laut Taskmanager habe ich eine konstante Auslastung von 18 MB. Ich habe die beiden Klassen schonmal vor einiger Zeit hier gepostet. Die anderen Schleifen unter 4.000 laufen auch sehr schnell durch. Ich greife bei den Results auf ein Array zurück.
//Gibt den Wert einer Zelle zurueck. Angegeben wird die Zeilennummer //und die FeldNummer oder der Feldname. const char * CMySQLQuery::GetCharValue(const unsigned int nRowIndex, const unsigned int nFieldIndex) { //Field-Index ausserhalb des gueltigen Bereichs if (nFieldIndex < 0 || nFieldIndex > m_lFieldsCount -1) return NULL; //Row-Index ausserhalb des gueltigen Bereichs if (nRowIndex < 0 || nRowIndex > m_lResultCount-1) return NULL; mysql_data_seek(m_MySQLResult, nRowIndex); m_MySQLRow = mysql_fetch_row (m_MySQLResult); return m_MySQLRow[nFieldIndex]; }Könnte es sein, dass return m_MySQLRow[1]; schneller ist als return m_MySQLRow[5000]; ?
-
Das sollte keinen Unterschied machen, ist ja nur einfache Zeigerarithmetik.
-
könnte es sein, dass du schwierigkeiten von der mysql seite her bekommst? versuch doch zum spass mal nicht alles auf einmal, sondern immer in 1000er blöcken. vielleicht geht das zügiger?
-
Ich weiss nicht, wie gut MySQL die Abfragen cached, aber du machst bei jedem Schleifendurchgang für jedes Feld ein neues mysql_data_seek und mysql_fetch_row. Ich denke, dass dies bei der GetCharValue Methode auch der Fall ist, die ein char const * als zweiten Parameter erhält.
MySQL wird bestimmt auch eine Art iterator anbieten, mit dem du durch den Datenbestand linear durchgehst. Das sollte am schnellsten sein.
-
Welches Indexfeld du zurück gibst, ist egal. Hat keinen Einfluss auf Performance.
Aber ein anderer Gedanke: wer sagt denn, das dein Programm daran schuld ist, und nicht die Datenbank selbst?
Vielleicht kann die das einfach nicht, mal so ebend so viele Statements hintereinander verarbeiten, ohne langsamer zu werden? Schliesslich ist die MySQL nur für ihre Query-Performance und nicht Schreib-Performance bekannt.Hast du vielleicht auch Transaktionen benutzt, die es langsamer machen könnten?
Würde es ansonst mit einer anderen DB ausprobieren, um einen Gegentest zu machen. Z.B. mit einer "richtigen" DB wie Oracle.

-
@ponto
soweit ich weiss wird mitmysql_data_seekder Cursor innerhalb des Results bewegt und mit
mysql_fetch_rowwird die Zeile, an die der Cursor steht kopiert. Mir sind leider keinen anderen Methoden bekannt, um an Ergebnisse heranzukommen.
@Artchi
MySQL ist leider vorgegeben
Der Datenbankserver läuft zu Testzwecken hier lokal und trotzdem habe ich diese Probleme. Ich bin schon am überlegen, welche Ausreden mir einfallen, um dieses als "normal" zu verkaufen 
@Korbinian
Ja, dass scheint mir bisher auch die beste Methode zu sein, aber ich weiss nur noch nicht wie. Ich frage ja mit "SELECT * FROM..." ab und bekomme als Ergebnis alle Daten. Wenn ich jetzt selektiere mit von bis, habe ich bestimmt wieder das gleiche in Grün.
-
SciFi schrieb:
@Artchi
MySQL ist leider vorgegeben
Der Datenbankserver läuft zu Testzwecken hier lokal und trotzdem habe ich diese Probleme. Ich bin schon am überlegen, welche Ausreden mir einfallen, um dieses als "normal" zu verkaufen 
Naja, was hat das denn mit lokal zu tun? Wenn der Server langsam wird, dann wird er langsam, egal ob lokal oder remote. Der Datentransfer von A nach B hat damit ja nichts zu tun. Sondern ganz einfach, das die DB nach jedem Insert vielleicht seine Daten organisieren muß... Indexieren und sonstigen pipapo. Bei 13.000 Inserts kann die DB schon gut ins Schwitzen kommen.
Die Datenbank ist ein Fremdprodukt, für uns alle eine Blackbox. Es wäre nichts ungewöhnliches, wenn die DB ganz einfach der Flaschenhals ist. Warum auch nicht? Nicht umsonst gibts Benchmark-Tests die Massendaten-Performance auf DBs testen. Nicht umsonst wird in Konzernen für Massendaten kein PC sondern ein IBM-Mainframe eingesetzt, der trotzdem Sekunden für ein Select braucht.
Da kann eine kostenlose MySQL schon mal schlapp machen.
Was passiert denn, wenn du das Insert-Statement raus nimmst? (also die ganze Codezeile) Wenn es dann nicht langsamer wird, ist die DB erstmal schuld.
Du solltest vielleicht mal andere MySQL-User fragen. Oder vielleicht gibts schon sowas in einer FAQ im Web?
-
Achja, warum machst du eigentlich 13.000 Inserts? Ganz dumm aber e4rnsthaft gefragt.

Dem Kunden oder deinem Chef würde ich einfach mal einen gleichen Test auf einer Oracle oder so präsentieren, falls diese schneller ist. Kann ich nicht versichern, aber dann haste Beweise das es nicht an deinem Code liegt.
-
Kopierst Du von mySQL nach mySQL?
Dann gib es folgende Alternativen:
insert into ... select (http://dev.mysql.com/doc/mysql/en/insert-select.html)
Load Data infile (http://dev.mysql.com/doc/mysql/en/load-data.html)Allgemein: http://dev.mysql.com/doc/mysql/en/insert-speed.html
Wenn Du von mySQL in einer andere DB (z.B. Oracle) kopierst, solltest Du Dir die import Features dieses DBMS anschauen. Und dann ist meistens am besten, über eine Datei zu importieren.
Eine weitere wichtige Rolle bei der Einfügegeschwindigkeit spielen Transaktionen.
Solche Fragen gehören eher ins Datenbank Forum...
-
schonmal ausprobiert einfach die forschleife umzudrehen? Also
for (nIndex = Import->GetResultCount()-1; nIndex >= 0; nIndex--)
-
niemand schrieb:
Kopierst Du von mySQL nach mySQL?
Load Data infileUi ui, hat MySQL keine Storedprocedures???

SPs haben heute wegen Preparedstatements fast keine Daseinsberechtigung, aber für solche Dinge sind sie natürlich weiter unerlässlich.
-
Also ich kopiere Daten von einer Software zu einer anderen. Die Datenbanken sind in vielen Punkten ähnlich, aber nicht gleich. Das ganze ist ein grösseres Software-Update.
Ich habe jetzt mal das INSERT Statement entfernt und siehe da, es rattert nur so durch. Darauf muss man erst mal kommen.
Ich werde mich mal durch den Link von 'niemand' durcharbeiten, aber ich habe jetzt schonmal einen Ansatzpunkt.
Vielen Dank an alle für Ihre Bemühungen !

Viele Grüße
Chrisitan
-
Artchi schrieb:
Ui ui, hat MySQL keine Storedprocedures???

Kommen afaik mit Verison 5, hilft MySql aber auch nix.
MfG SideWinder
-
SciFi schrieb:
Ich frage ja mit "SELECT * FROM..." ab und bekomme als Ergebnis alle Daten. Wenn ich jetzt selektiere mit von bis, habe ich bestimmt wieder das gleiche in Grün.
du könntest es z.b. so machen
# pseudo... last_id = -1; while (have rows) do SELECT * from table WHERE id > lastid ORDER BY id ASC LIMIT 1000 while (have rows) do # insert query processed_ids = row.id; end endhift das?
-
Ich denke schon, aber MySQL hat wohl Probleme mit der Menge an Daten die ich da rüberschaufel. Ich müsste dann noch den 1000 eine kleine Pause machen.
Momentan suche ich noch nach INSERT Ergänzungen.