QSqlQuery und execBatch mit problematischen '

Alles rund um die Programmierung mit Qt
Antworten
KartoffelKiffer
Beiträge: 101
Registriert: 27. Februar 2008 15:59

QSqlQuery und execBatch mit problematischen '

Beitrag 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
KartoffelKiffer
Beiträge: 101
Registriert: 27. Februar 2008 15:59

Beitrag 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.
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag 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'.
KartoffelKiffer
Beiträge: 101
Registriert: 27. Februar 2008 15:59

Beitrag 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!
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag 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.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
KartoffelKiffer
Beiträge: 101
Registriert: 27. Februar 2008 15:59

Beitrag 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!
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag 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!
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag 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?
KartoffelKiffer
Beiträge: 101
Registriert: 27. Februar 2008 15:59

Beitrag 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.
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag 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.
DBGTMaster
Beiträge: 190
Registriert: 19. August 2010 10:00

Beitrag 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?!
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag 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
KartoffelKiffer
Beiträge: 101
Registriert: 27. Februar 2008 15:59

Beitrag 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!
KartoffelKiffer
Beiträge: 101
Registriert: 27. Februar 2008 15:59

Beitrag 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!
KartoffelKiffer
Beiträge: 101
Registriert: 27. Februar 2008 15:59

Beitrag 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!
Antworten