Moje pytanie pochodzi bezpośrednio z this, ale interesuje mnie tylko UPDATE i tylko to.Poprawić wydajność SQLite za aktualizację na sekundę?
Mam aplikacja napisana w C/C++
co sprawia ciężki wykorzystanie SQLite
, głównie SELECT/UPDATE
, na bardzo częstych odstępach (około 20 zapytań co 0,5 do 1 sekundy)
Moja baza danych nie jest duży, około zapisy w momentach, tutaj jest struktura tabeli:
CREATE TABLE player (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(64) UNIQUE,
stats VARBINARY,
rules VARBINARY
);
do tej pory nie stosowane transactions
bo poprawa kodu i chciał ukłucie ility raczej wydajność.
Potem mierzono moją wydajność bazy danych jedynie poprzez wykonanie 10 update
zapytań, co następuje (w pętli różnych wartości):
// 10 times execution of this
UPDATE player SET stats = ? WHERE (name = ?)
gdzie stats
jest JSON dokładnie 150 znaków i name
wynosi od 5-10 postacie.
bez operacji wynik jest nie do przyjęcia - o jeden pełny sekund (0,096 każdy)
z transakcji, czas spada razy x7.5: - 0,11 - 0,16 sekundy (0,013 każdy)
Próbowałem usunąć dużą część bazy danych i/lub zmienić kolejność/usunięcie kolumn, aby zobaczyć, czy to coś zmieni, ale nie. Otrzymuję powyższe liczby, nawet jeśli baza danych zawiera tylko 100 rekordów (testowanych).
Potem próbował gry z PRAGMA
opcji:
PRAGMA synchronous = NORMAL
PRAGMA journal_mode = MEMORY
dał mi mniejsze razy, ale nie zawsze, bardziej jak około 0,08 - 0,14 sekundy
PRAGMA synchronous = OFF
PRAGMA journal_mode = MEMORY
Wreszcie dał mi bardzo małe czasy około 0.002 - 0.003 sekund, ale nie chcę go używać, ponieważ moja aplikacja zapisuje bazę danych co sekundę i istnieje duża szansa na uszkodzenie bazy danych o n OS/brak zasilania.
Mój kod C SQLite
zapytań jest: (Komentarze/obsługa błędów/niepowiązanych części pominięto)
// start transaction
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);
// query
sqlite3_stmt *statement = NULL;
int out = sqlite3_prepare_v2(query.c_str(), -1, &statement, NULL);
// bindings
for(size_t x = 0, sz = bindings.size(); x < sz; x++) {
out = sqlite3_bind_text(statement, x+1, bindings[x].text_value.c_str(), bindings[x].text_value.size(), SQLITE_TRANSIENT);
...
}
// execute
out = sqlite3_step(statement);
if (out != SQLITE_OK) {
// should finalize the query no mind the error
if (statement != NULL) {
sqlite3_finalize(statement);
}
}
// end the transaction
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
Jak widać, jest to dość typowe TABLE
, rejestruje ilość jest niewielka i robię zwykły prosty UPDATE
dokładnie 10 razy. Czy jest coś jeszcze co mogłem zrobić, aby zmniejszyć mój czas? Używam najnowszego SQLite 3.16.2
.
UWAGA: synchronizacją powyżej pochodzą bezpośrednio z jednego
END TRANSACTION
zapytania.Kwerendy są wykonywane w prostej transakcji i jestem za pomocą przygotowanego oświadczenia.
UPDATE:
Przeprowadziłem kilka testów z włączonym transakcji oraz osób niepełnosprawnych i różnych aktualizacji liczyć. Przeprowadziłem testy z następującymi ustawieniami:
VACUUM;
PRAGMA synchronous = NORMAL; -- def: FULL
PRAGMA journal_mode = WAL; -- def: DELETE
PRAGMA page_size = 4096; -- def: 1024
Wyniki następująco:
żadnych transakcji (10 Updates)
- 0,30800 sek (0,0308 za update)
- 0,30200 sekundy
- 0.36200 secs
- 0.28600 s ECS
żadne transakcje (100 Updates)
- 2.64400 sek (0.02644 każda aktualizacja)
- 2.61200 sek
- 2.76400 sek
- 2.68700 sek
bez transacti Świat (1000 aktualizacje)
- 28.02800 sek (0,028 każdy update)
- 27.73700 sek
- ..
z transakcji (10 aktualizacje)
- 0,12800 secs (0.0128 każda aktualizacja)
- 0,08100 sekund
- 0.16400 sek
- 0,10400 sekund
z transakcji (100 aktualizacje)
- 0,088 sek (0,00088 każda aktualizacja)
- 0,091 sek
- 0.052 sek
- 0.101 sek
z transakcjami (1000 Updates)
- 0.08900 sek (0.000089 każda aktualizacja)
- 0.15000 sek
- 0.11000 sek
- 0.09100 sek
Moje wnioski są następujące: transactions
nie ma sensu w time cost per query
. Być może czasy stają się większe dzięki ogromnej liczbie aktualizacji, ale nie interesują mnie te liczby. Dosłownie nie ma różnicy w kosztach czasowych między aktualizacjami 10 a 1000 w pojedynczej transakcji. Jednak zastanawiam się, czy jest to ograniczenie sprzętowe na moim komputerze i nie mogę wiele zrobić. Wygląda na to, że nie mogę zejść poniżej poziomu ~100
milisekund przy użyciu pojedynczej transakcji i aktualizacji 10-1000, nawet przy użyciu WAL.
Bez transakcji nie ma ustalonego kosztu czasu około 0.025
sekund.
Jeśli używasz languiage C/C++, używać odpowiedniego znacznika. W przeciwnym razie wygląda na C++, a nie na ** inny ** język C! I to nie jest strona z recenzowaniem kodu. – Olaf
@Olaf, jedyne rzeczy 'C++' to 'std :: string'; reszta to 'C'. Szczególnie podkreślam to powyżej. Po drugie nie chcę, aby ktoś przejrzał mój kod, chcę lepszego podejścia do SQLite, aby rozwiązać mój problem – user6096479
Nie ** nie ** kompiluje się jako C, więc nie jest C. Tylko dlatego, że masz taką samą składnię/gramatykę nie oznacza, że masz tę samą semantykę! Ten, kto mówi ci, że C++ to "C z klasami", jest zupełnie błędny i nie zna przynajmniej jednego z nich na tyle dobrze, by napisać nietrywialny kod. – Olaf