Używam modułu SQL Qt 5.3.1 (Win 7, VS2013) do wstawiania danych do bazy danych MySQL 5.6. Po zauważeniu pewnych problemów z wydajnością wykonuję trzy testowe fragmenty kodu i mierzy ich środowisko wykonawcze, aby lepiej zrozumieć wydajność SQL. Wynik jest mylący.Wydajność przy korzystaniu z trybu wsadowego Qt/MySQL
Do testowania użyłem tabeli "testowej" zawierającej kolumnę VARCHAR "test" i unikalnie zwiększany identyfikator dla każdego wiersza.
Pierwszy fragment wygląda w zasadzie tak:
const QString uploadQueryString("INSERT INTO test (test) VALUES ('%1')");
for (int i=0; i<1000; i++)
{
QSqlQuery uploadQuery(uploadQueryString.arg("A: test text"), dataBase);
if (uploadQuery.lastError().isValid())
{
qDebug() << tr("Query execution failed (%1)").arg(uploadQuery.lastError().text());
}
}
Drugi tak:
const QString uploadQueryString("INSERT INTO test (test) VALUES %1");
QStringList values;
for (int j=0; j<1000; j++)
{
values.append("\"B: test text\"");
}
QString valuesString = "("+ContainerToString(values, "), (")+")";
QSqlQuery uploadQuery(uploadQueryString.arg(valuesString), dataBase);
if (uploadQuery.lastError().isValid())
{
qDebug() << tr("Query execution failed (%1)").arg(uploadQuery.lastError().text());
}
Trzecia tak:
const QString uploadQueryString("INSERT INTO test (test) VALUES (:values)");
QVariantList values;
for (int j=0; j<1000; j++)
{
values.append("C: test text");
}
QSqlQuery batchQuery(dataBase);
if (batchQuery.prepare(uploadQueryString))
{
batchQuery.bindValue(":values", values);
if (!batchQuery.execBatch())
{
qDebug() << tr("Batch query execution failed (%1)").arg(batchQuery.lastError().text());
}
}
else
{
qDebug() << tr("Unable to prepare batch query");
}
I wykonany każdy z tych fragmentów (łącznie z kodem otwartym/zamkniętym) 10 razy:
1. 10x1000 basic inserts
Ticks delta: 318617 ms; Kernel delta: 358 ms; User delta: 1201 ms; Process delta 1559 ms
2. 10x1000 by value list insert
Ticks delta: 3011 ms; Kernel delta: 0 ms; User delta: 46 ms; Process delta 46 ms
3. 10x1000 by batch insert
Ticks delta: 631679 ms; Kernel delta: 811 ms; User delta: 998 ms; Process delta 1809 ms
"Delta końcówek" to czas potrzebny na każdy fragment. "Delta jądra" i "User delta" to faktycznie aktywny czas przetwarzania użytkownika i jądra, podczas gdy "Process delta" to suma czasu jądra i użytkownika.
Pierwszy wynik jest zgodny z oczekiwaniem: Potrzeba dużo czasu (zwłaszcza ze względu na opóźnienia), aby wykonać pojedyncze zapytania 10x1000.
Drugi wynik również jest taki, jak oczekiwano: bardzo szybko można wykonać pojedyncze zapytanie zawierające wszystkie wiersze naraz.
Niestety trzeci wynik całkowicie mnie myli: Spodziewałem się, że tryb wsadowy będzie znacznie szybszy! Nawet w najgorszym przypadku (jeśli wykonanie wsadu jest symulowane przez sterownik za pomocą pojedynczych zapytań, jak mówi dokumentacja Qt), powinien on być równie wolny jak pierwszy fragment. W rzeczywistości potrzebuje dwa razy więcej czasu!
Co właściwie robi wykonanie wsadowe w Qt/MySQL? Czy istnieje sposób na poprawę wydajności wymuszania wsadowego? Jak to możliwe, że execBatch() działa o wiele gorzej niż wykonywanie pojedynczych zapytań?
Czy możesz w dowolnym czasie ustawić wartość 'bind_value()'? Nie w pełni rozumiem trzecie podejście, ale wydaje się niezwykłe wiązanie obiektu listy ciągów do parametru SQL. Mogą być dodatkowe warstwy analizy po stronie klienta, których nie widzisz. –
Wywołania bindowania są zwracane niemal natychmiast. – Silicomancer