Optimisation de l'accès aux bases
Bonjour,
je découvre Qt depuis quelques mois. Après la découverte, j'en suis au stade de l'optimisation des codes et à la comparaison avec mon point de départ, Lazarus.
Mon premier problème "sensible" est l'insertion dans les BDD que je trouve lente aussi bien dans les tables MySQL myISAM -donc sans transaction- que dans les tables MySQL innoDB, et de manière moins sensible (en valeur absolue) dans les tables PostgreSQL. Mes bases sont hébergées sur un serveur dédié hébergé Linux 64 bits : MySQL 5.5 et pgSQL 9.1
En MySQL, c'est une véritable catastrophe. La latence est très importante. En pgSQL c'est nettement moins bien qu'en Lazarus... C'est plus rapide qu'en MySQL mais comme avec ce dernier, le temps d'insertion est toujours quasiment le double. 8O
Les tables de test contiennent 2 varchar(20). La première est la clé primaire, la seconde est une clé unique.
Voici mes codes de tests actuels en PostgreSQL.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
| ##include "mainwindow.h"
#include <QApplication>
#include <QDebug>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QSqlTableModel>
#include <QSqlRecord>
#include <QDateTime>
#include <QString>
#include <QTime>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
db.setHostName("xxx.xxx.xxx.xxx");
db.setUserName("xxxxxxxxxxr");
db.setPassword("xxxxxxxxxx");
db.setDatabaseName("xxxxxxxxxx");
qDebug() << db.isValid();
if (!db.open()) {
qDebug() << db.lastError();
qDebug() << "Connexion FAILED.";
qDebug() << "Driver disponible: " << db.drivers();
qDebug() << "Driver fonctionnel(QPSQL): " << db.isDriverAvailable("QPSQL");
qDebug() << "numero de l'erreur: " << db.lastError().number();
qDebug() << "database erreur: " << db.lastError().databaseText();
qDebug() << "driver erreur: " << db.lastError().driverText();
qDebug() << "type erreur: " << db.lastError().type();
qFatal("Impossible de se connecter à la base.");
}
qDebug() << "Vous êtes connecté à " << db.hostName();
int dbcount;
QSqlQuery query(db);
query.exec("SELECT COUNT(*) FROM test2012");
while (query.next()) {
dbcount = query.value(0).toInt();
qDebug() << dbcount;
}
bool bOK;
int rowsToAdd = 100; //Insertion de 100 lignes
QString str;
QDateTime dtnow;
QTime time;
bOK = true;
if (db.transaction()) {
time.start();
query.prepare("INSERT INTO test2012 (teid, tenom) VALUES (:teid, :tenom)");
for (int i = 0 ; i < rowsToAdd ; i++) {
dtnow = QDateTime::currentDateTime();
str = dtnow.toString("yyyyMMddHHmmsszzz")+ QString::number(i);
query.bindValue(":teid", str);
query.bindValue(":tenom", str);
if (!query.exec()) {
bOK = false;
break;
}
}
if (bOK == true) {
db.commit();
qDebug() << "Succès Insertion 100 - " << time.elapsed() << "millisecondes";
} else {
db.rollback();
qDebug() << "Echec Insertion 100 - " << time.elapsed() << "millisecondes";
}
}
query.exec("SELECT COUNT(*) FROM test2012");
while (query.next()) {
dbcount = query.value(0).toInt();
qDebug() << dbcount;
}
time.start();
QSqlTableModel model(0, db);
model.setEditStrategy(QSqlTableModel::OnManualSubmit);
model.setTable("test2012");
model.select();
int rowCount = model.rowCount();
qDebug() << rowCount;
model.database().transaction();
if(!model.insertRows(rowCount, rowsToAdd)) {
qDebug() << "insertRows" << model.lastError().text();
return 0;
}
int idIndex = model.record().indexOf("teid");
int nomIndex = model.record().indexOf("tenom");
for (int i = 0 ; i < rowsToAdd ; i++) {
dtnow = QDateTime::currentDateTime();
str = dtnow.toString("yyyyMMddHHmmsszzz")+ QString::number(i);
model.setData(model.index(rowCount +i, idIndex),str);
model.setData(model.index(rowCount +i, nomIndex),str);
}
if(model.submitAll()) {
model.database().commit();
qDebug() << "Ecriture 100 - " << time.elapsed() << "millisecondes";
} else {
model.database().rollback();
qDebug() << "Database Write Error" <<
"The database reported an error: " <<
model.lastError().text();
}
query.exec("SELECT COUNT(*) FROM test2012");
while (query.next()) {
dbcount = query.value(0).toInt();
qDebug() << dbcount;
}
qDebug() << "Fermeture de la base.";
if (db.isOpen()) {
db.close();
}
return a.exec();
} |
La réponse est
Citation:
true
Vous êtes connecté à "xxx.xxx.xxx.xxx"
7626
Succès Insertion 100 - 4889 millisecondes
7726
7726
Ecriture 100 - 6292 millisecondes
7826
Fermeture de la base.
5 secondes, c'est beaucoup comparé à Lazarus qui sur la même base et sur le même appareil client (Win7-32 bits) met environ 2 secondes pour faire le même travail. Evidemment avec cette première méthode d'insertion, je pourrais certainement utiliser les tuples mais alors je ne sais pas comment utiliser une barre de progression dans cette configuration. Et puis à priori les tuples cela ne fonctionne pas avec la 2ème méthode, les TableSQLmodel.
Et avec TableSQLmodel, 6 secondes, c'est encore plus long même si j'ai bien compris que TableSQLmodel n'est pas à privilégier pour ce type d'insertion.
En ce qui concerne MySQL, c'est franchement mauvais ! En innoDB (avec AutoCommit = 0) l'ajout de 100 enregistrements requiert environ 9 s (8896ms). Sur une table identique en myISAM, guère mieux (8844 ms) :aie:... Avec Lazarus, moins de 5 secondes quelque soit la table (innoDB ou myISAM). Ce que je trouve étonnant, c'est que quelque soit la méthode utilisée, il est impossible de s'approcher de Lazarus dès qu'on utilise Qt alors qu'en C++ sans Qt (#include <winsock.h> #include <MYSQL/mysql.h>), je n'ai pas noté de différence significative avec Lazarus.
Bref, j'essaye de savoir si les codes sont adaptés, s'il y a moyen de les améliorer ou s'il existe une méthode plus pertinente (à partir de Qt)...
Mon deuxième sujet, en cours de test, est la lecture des tables et plus précisément l'utilisation de Fetchrow et des threads... Et je dois reconnaître que je patauge au niveau de la documentation. Il y en a un peu mais je ne trouve rien vraiment d'exploitable... Si quelqu'un(e) a une bonne adresse, je suis preneur.
Merci. Cordialement. Gilles