Seite 1 von 1
QSqlQuery und execBatch mit problematischen '
Verfasst: 26. November 2010 12:51
von KartoffelKiffer
Hallo,
ich versuche nun seit heute Morgen mein Programm ein wenig schneller zu bekommen und wollte nun QSqlQuery::execBatch() verwenden.
Ein kleiner Test veranschaulicht meine Vorgehensweise.
Code: Alles auswählen
QVarianList tables, columns, values;
tables << "tab1" << "tab2";
columns << "col11, col12, col13" << "col21, col22, col23";
values << "11, 'value12', 'value13'" << "21, 22, 'value23'";
QSqlQuery q;
q.prepare("INSERT INTO ? (?) VALUES (?)");
q.addBindValue(tables);
q.addBindValue(columns);
q.addBindValue(values);
if (q.execBatch())
{
qDebug(q.lastError().databaseText().toAscii());
}
Die Variable values weiß also schon vorher, welche Werte entsprechend mit ' gekennzeichnet werden müssen.
Führe ich die Abfrage nun aus, erscheint folgende Fehlermeldung im Debugger:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''tab1' ('col11, col12, col13' at line 1
Es werden also scheinbar eigenständig quotes den einzelnen Werten hinzugefügt, weshalb meine Abfrage auch falsch ist.
Richtig:
INSERT INTO tab1 (col11, col12, col13) VALUES(11, 'value12', 'value13')
Und so scheint es zu sein
INSERT INTO 'tab1' ('col11, col12, col13') VALUES('11, 'value12', 'value13'')
Kann ich execBatch nun überhaupt so verwenden, wie ich es vor habe, oder ist es für diesen Anwendungsfall nicht vorgesehen? Es müsste für mich die einfache Lösung geben, dass die automatische "Quotierung" weg gelassen wird.
Ich hoffe mir kann geholfen werden.
Gruß, KK
Verfasst: 26. November 2010 14:27
von KartoffelKiffer
Hallo,
bisher kannte ich die Funktion QSqlQuery::executedQuery() gar nicht, diese bestätigt mir, dass die Anweisungen, wie oben beschrieben, fehlerhaft abgesetzt werden.
Verfasst: 26. November 2010 15:56
von upsala
execBatch macht nichts anderes als ein preparedStatement auszuführen (oder etwas was sich so verhält).
Bei prepared Statements werden die Parameter immer passend 'escaped'.
Verfasst: 26. November 2010 16:58
von KartoffelKiffer
.... Mist!
http://dev.mysql.com/tech-resources/art ... ments.html
Ich habe momentan nämlich das Problem, dass ich per Schleife und einfachen QSqlQuery::exec-Anweisungen sämtliche INSERT-Statements sequenziell an die Datenbank schicke.
Leider ist hier der Engpass, denn das Programm, sowie die Datenbank, gähnen bloß. CPU-Last jeweils unter 10%.
Ich hätte gedacht, dass das Zusammenführen als Batch da etwas mehr Performance bringt. Dass also erst die Statements vorbereitet werden ( CPU-intensiv ) und diese danach an die Datenbank geschickt werden. Insgesamt läuft erst das Programm warm, danach die Datenbank. Im Moment frösteln sie leider bloß.
Falls jemand noch einen Tipp für mich hat, immer her damit, ich bin für sämtliche Anregungen dankbar!
Verfasst: 26. November 2010 17:15
von Christian81
Eine Transaktion hat bei uns Wunder gewirkt bei vielen einzelnen Queries - ist ja auch klar - so wird alles erst gesammelt und dann beim commit einmal geschrieben.
Verfasst: 26. November 2010 17:17
von KartoffelKiffer
Der Tipp ist nicht verkehrt!
Wir nutzen zwar MyISAM aber das lässt sich temporär auch auf InnoDB stellen!
Gleich mal versuchen!
Verfasst: 26. November 2010 17:19
von solarix
Es gibt noch zwei weitere Varianten um das ganze zu Beschleunigen:
1. Mehrere VALUE-Blöcke pro INSERT..
2. DELAYED-Inserts :
http://dev.mysql.com/doc/refman/5.1/en/ ... layed.html
hth!
Verfasst: 26. November 2010 18:08
von upsala
Wenn das Programm und die Datenbank nur bei 10% Auslastung liegen, scheint es an den Festplatten bzw. dem Netzwerk zu liegen.
Wenn es InnoDB-Tabellen sind bringt natürlich die Zusammenfassung zu größeren Transaktionen auf jeden Fall etwas. Bei MyIsam bringt es etwas die VALUES-Blöcke zusammenzufassen wie solarix geschrieben hat.
Wenn es Geschwindigkeitsprobleme geben sollte ist aber Kontraproduktiv auf InnoDB umzustellen.
DELAYED bringt nur dann etwas, wenn viele Clients schreiben und dann auch nur bei MyISAM.
Wie viele Statements führt deine Datenbank im Moment pro Sekunde aus?
Verfasst: 29. November 2010 14:18
von KartoffelKiffer
Hallo upsala,
ich habe eine SSD im Einsatz und greife auf einen lokalen Server mittels 127.0.0.1 zu. Daran sollte es nicht liegen?! ( ist eher als Frage formuliert )
INSERT_DELAYED kann genau das sein, was ich später im Einsatz benötige, da wir etwa 30 Clients haben, die 24/7 auf die Datenbank schreiben.
Das Zusammenführen der einzelnen INSERTs kann mir mitunter enorme Vorteile bringen!
Ich brauche zurzeit etwa 35ms für das Eintragen von einem Datensatz im Mittel, für insgesamt fünf Tabellen. Es wären also ~7ms pro Tabelle pro Datensatz.
Das Programm generiert willkürliche Daten für die INSERT-Statements. Kommentiere ich den Teil aus, in dem die INSERT-Anweisung an die Datenbank gesendet wird, läuft mein Programm ( optimiert ) auf knapp 100% CPU-Last. Sende ich die Anweisung jedoch an die Datenbank, ist das Programm kaum auf Last. Das INSERT-Statement arbeitet also scheinbar wie eine Art Sleep auf das Programm. Nur leider kümmert das die Datenbank auch überhaupt nicht - die CPU-Last bleibt gering.
Wäre die Datenbank nun in Beschlag durch die INSERTs, dann würde mich die ganze Problematik nicht interessieren. Nur leider schläft sie, wie mein Programm auch. Was mich natürlich stört, da ich Performance verschenke!
Mehrere VALUES habe ich noch nicht verwendet, das steht aber direkt an.
Verfasst: 29. November 2010 16:49
von upsala
127.0.0.1 und localhost bedeuten bei MySQL folgendes: 127.0.0.1 geht über den Netzwerk-Stack. localhost über eine Socket-Verbindung.
Ob dies einen Geschwindigkeitsunterschied ausmacht, kann jetzt allerdings nicht beurteilen.
Zu den Zeiten habe ich momentan folgenden Richtwert:
DualCore 2GHz Xeon schafft in eine InnoDB-Tabelle 6000-9000 Inserts pro Sekunde (unter Linux mit ca. 150 Clients). Zeilenlänge ca. 200Byte.
Verfasst: 29. November 2010 17:32
von DBGTMaster
upsala hat geschrieben:127.0.0.1 und localhost bedeuten bei MySQL folgendes: 127.0.0.1 geht über den Netzwerk-Stack. localhost über eine Socket-Verbindung.
Ob dies einen Geschwindigkeitsunterschied ausmacht, kann jetzt allerdings nicht beurteilen.
Zu den Zeiten habe ich momentan folgenden Richtwert:
DualCore 2GHz Xeon schafft in eine InnoDB-Tabelle 6000-9000 Inserts pro Sekunde (unter Linux mit ca. 150 Clients). Zeilenlänge ca. 200Byte.
ich bin der meinung, dass localhost und 127.0.0.1 das selbe sind, nur das eine dns auflösung vor der verbindung geschieht?!
Verfasst: 29. November 2010 18:32
von upsala
On Unix, MySQL programs treat the host name localhost specially, in a way that is likely different from what you expect compared to other network-based programs. For connections to localhost, MySQL programs attempt to connect to the local server by using a Unix socket file.
Quelle:
http://dev.mysql.com/doc/refman/5.0/en/connecting.html
Verfasst: 29. November 2010 18:56
von KartoffelKiffer
Na toll, jetzt ist das Programm zu schnell für meine Random-Funktion; der seed löst bloß nach ms auf... Aber das ist ein anderes Thema!
Zur " Problematik " localhost: Ich habe damals mit JDBC ( Java ) gearbeitet, wo ein Fehler bekannt war, dass es unter Umständen sein kann, dass die DNS-Auflösung nicht korrekt funktioniert und man entsprechend 127.0.0.1 verwenden sollte. Darum das Angewöhnen die IP-Adresse aus zu schreiben.
Wir können unsere wie-viele-Werte-kommen-sekündlich-in-die-Datenbank-Aussage leider nicht vertiefen, da bei mir noch Rechenoperationen zwischen den INSERTs liegen, und ich bislang nur einen Client ( = Server ) verwende.
Meine INSERT-Statements sehen nun wie folgt aus:
INSERT INTO tab1 (col1, col2) VALUES (1, "1"), (2, "2")
usw.
Also jede Iteration hängt ein ", (...)" an das Statement an. Das ist natürlich verflucht unschön und eine echte Last für den SQL-Parser, aber das dürfte das kleinste Übel darstellen. ( mal sehen, wie lang ein SQL-Statement sein darf! )
Ich muss leider Feierabend machen, darum berichte ich morgen, was die überarbeitete Funktion gegeben hat.
Ich bedanke mich schon mal bei allen Beteiligten!
Verfasst: 2. Dezember 2010 11:49
von KartoffelKiffer
So, ich bin jetzt runter auf 3ms pro Durchlauf für insgesamt 332 Bytes auf fünf Tabellen verteilt.
Ich generiere INSERT-Anweisungen jetzt mit zig VALUES, wie unten schon beschrieben. Zusätzlich habe ich aber noch eine Begrenzung eingebaut, die die MAX_VALUES kontrolliert. Am Schnellsten waren die INSERT-Anweisungen mit etwa 1.000 VALUES.
INSERT INTO tab1 (col1, col2) VALUES ("col1", 1), ("col1", 2), ("col1", 3), ...("col1", 1000) Das funktioniert trotz der Fülle an VALUES erstaunlich gut!
Ich danke allen Beteiligten nochmals für ihr Engagement!
Verfasst: 2. Dezember 2010 15:22
von KartoffelKiffer
PS: Wie sich einige sicherlich denken können, hier auch nochmals der Tipp
http://dev.mysql.com/doc/refman/5.1/de/ ... ables.html
Der Puffer für die MySQL-Anfrage muss erhöht werden, da sonst Timeouts ála " MySQL server has gone away " drohen!