2017-02-03 8 views
5

Meine Frage kommt direkt aus this ein, obwohl ich nur an UPDATE interessiert bin und nur das.Verbessern UPDATE-pro-Sekunde-Leistung von SQLite?

Ich habe eine Anwendung in C/C++ geschrieben, die starke Nutzung von SQLite macht, meist SELECT/UPDATE, auf einem sehr häufigen Intervall (ca. 20 Abfragen alle 0,5 bis 1 Sekunde)

Meine Datenbank ist nicht groß, etwa hier ist 2500 Aufzeichnungen in den Momenten, die Struktur der Tabelle:

CREATE TABLE player (
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    name VARCHAR(64) UNIQUE, 
    stats VARBINARY, 
    rules VARBINARY 
); 

bis zu diesem Punkt, den ich nicht transactions verwendet habe, weil ich den Code wurde verbessert und wollte stab eher Leistung.

Dann maß ich meine Datenbank-Performance von lediglich 10 update Ausführen von Abfragen, die folgenden (in einer Schleife von unterschiedlichen Werten): ist

// 10 times execution of this 
UPDATE player SET stats = ? WHERE (name = ?) 

wo stats ist ein JSON von genau 150 Zeichen und name von 5 bis 10 Figuren.

Ohne Transaktionen, ist das Ergebnis nicht akzeptabel: - etwa 1 volle Sekunde (0,096 each)

mit den Transaktionen, fällt die Zeit x7.5 Zeiten: - etwa 0,11 - 0,16 Sekunden (0,013 each)

Ich versuchte, einen großen Teil der Datenbank zu löschen und/oder Spalten neu anzuordnen/zu löschen, um zu sehen, ob sich das ändert, aber es tat es nicht. Ich bekomme die obigen Zahlen auch wenn die Datenbank nur 100 Datensätze (getestet) enthält.

Ich habe dann versucht, mit PRAGMA Optionen spielen:

PRAGMA synchronous = NORMAL 
PRAGMA journal_mode = MEMORY 

Gab mir kleinere Zeiten, aber nicht immer, eher wie etwa 0,08 - 0,14 Sekunden

PRAGMA synchronous = OFF 
PRAGMA journal_mode = MEMORY 

Schließlich gab mir extrem kleine Zeiten etwa 0,002 - 0,003 Sekunden, aber ich möchte es nicht verwenden, da meine Anwendung die Datenbank jede Sekunde speichert und die Wahrscheinlichkeit einer beschädigten Datenbank sehr hoch ist n OS/Stromausfall.

Mein C SQLite Code für Abfragen ist: (Kommentare/Fehlerbehandlung/ohne Bezug Teile weggelassen)

// 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); 

Wie Sie sehen, es ist ein ziemlich typisches TABLE zeichnet Zahl ist klein und ich bin ein einfache einfach tun UPDATE genau 10 mal. Gibt es noch etwas, was ich tun könnte, um meine UPDATE Zeiten zu verringern? Ich benutze die neueste SQLite 3.16.2.

HINWEIS: Die Zeiten oben sind direkt aus einer einzigen Abfrage END TRANSACTION kommen.Abfragen werden in einer einfachen Transaktion durchgeführt und ich bin mit einer vorbereiteten Anweisung.

UPDATE:

Ich führte einige Tests mit der Transaktion aktiviert und deaktiviert und verschiedene Updates zählen. Ich führte die Tests mit den folgenden Einstellungen:

VACUUM; 
PRAGMA synchronous = NORMAL; -- def: FULL 
PRAGMA journal_mode = WAL; -- def: DELETE 
PRAGMA page_size = 4096;  -- def: 1024 

Die Ergebnisse folgt:

keine Transaktionen (10 Updates)

  • 0,30800 Sekunden (0,0308 pro Update)
  • 0,30200 Sekunden
  • 0,36200 Sekunden
  • 0,28600 s ecs

keine Transaktionen (100 Updates)

  • 2,64400 Sekunden (0,02644 jedes Update)
  • 2,61200 Sekunden
  • 2,76400 Sekunden
  • 2,68700 Sekunden

keine transacti ons (1000 Updates)

  • 28,02800 Sekunden (0,028 jedes Update)
  • 27,73700 Sekunden
  • ..

mit Transaktionen (10 Updates)

  • 0,12800 Sekunden (0.0128 jedes Update)
  • 0,08100 Sekunden
  • 0,16400 Sekunden
  • 0,10400 Sekunden

mit Transaktionen (100 Updates)

  • 0,088 Sekunden (0,00088 jedes Update)
  • 0,091 Sekunden
  • 0.052 Sekunden
  • 0.101 Sekunden

mit Transaktionen (1000 Updates)

  • 0,08900 Sekunden (0,000089 jedes Update)
  • 0,15000 Sekunden
  • 0,11000 Sekunden
  • 0,09100 Sekunden

Meine Schlussfolgerungen sind das mit transactions gibt es keinen Sinn in time cost per query. Vielleicht wird die Zeit mit einer riesigen Anzahl von Updates größer, aber ich bin nicht an diesen Zahlen interessiert. Es gibt buchstäblich keinen Zeitunterschied zwischen 10 und 1000 Updates für eine einzige Transaktion. Ich frage mich jedoch, ob dies ein Hardware-Limit auf meinem Rechner ist und nicht viel bewirken kann. Es scheint, ich kann nicht unter ~100 Millisekunden mit einer einzigen Transaktion und Bereich 10-1000 Updates gehen, auch mit WAL.

Ohne Transaktionen gibt es einen festen Zeitaufwand von etwa 0.025 Sekunden.

+0

Wenn Sie die languiage C/C++ verwenden, verwenden Sie den richtigen Tag. Ansonsten sieht es aus wie C++, nicht die ** andere ** Sprache C! Und dies ist keine Code-Review-Website. – Olaf

+0

@Olaf, das einzige 'C++' Zeug ist ein 'std :: string'; der Rest ist "C". Ich betone das ausdrücklich oben. Zweitens möchte ich nicht, dass jemand meinen Code überprüft, ich möchte einen besseren Ansatz von SQLite, um mein Problem zu lösen – user6096479

+4

Es tut ** nicht ** kompilieren als C, also ist es nicht C. Nur weil Sie die gleiche Syntax/Grammatik haben bedeutet nicht, dass Sie die gleiche Semantik haben! Wer sagt, dass C++ "C with classes" ist, ist schlicht falsch und kennt zumindest einen von ihnen nicht gut genug, um nicht-trivialen Code zu schreiben. – Olaf

Antwort

3

Bei so kleinen Datenmengen, die Zeit für den Datenbankbetrieb selbst ist unbedeutend; Was Sie messen, ist der Transaktionsaufwand (die Zeit, die benötigt wird, um den Schreibvorgang auf die Festplatte zu erzwingen), was vom Betriebssystem, dem Dateisystem und der Hardware abhängt.

Wenn Sie mit seinen Einschränkungen (meist kein Netzwerk) leben können, können Sie asynchrone Schreibvorgänge verwenden, indem Sie WAL mode aktivieren.

+0

Ich versuchte 'WAL', indem ich' PRAGMA synchrone = NORMAL' und 'PRAGMA journal_mode = WAL' einstellte, aber ich habe keine Verbesserung bekommen, ich meine überhaupt. Ich brauche kein Netzwerk und ich möchte von WAL profitieren, ich bekomme nur keinen Gewinn (vielleicht sind einige zusätzliche Optionen erforderlich?!?). Auf der anderen Seite muss ich nur maximal 20 Abfragen pro Maximum von 1 Sekunde "updaten". Sie (Abfragen) können weniger sein, können aber nicht mehr sein. Es scheint mir, dass die Verwendung von 'synchronous = NORMAL oder FULL' die Schranke von' 10 ms/update query' nicht durchbrechen kann, es sei denn, ich entscheide mich für OS, um die Sicherheit zu verbessern. – user6096479

+0

Um zu prüfen, ob WAL aktiviert ist, führen Sie 'PRAGMA journal_mode;' aus. –

+0

Ich weiß nicht warum, aber mein 'PRAGMA journal_mode = WAL' wurde bei meinem C-Aufruf überhaupt nicht abgefragt, also blieb der Modus' DELETE'. Wenn ich die Abfrage auf 'PHPLiteAdmin' normal ausgeführt habe. Jedoch meine Zeiten leicht auf etwa 0,07-0,11 reduziert, was OK scheint. – user6096479

3

Sie können immer noch durch die Zeit beschränkt werden, die es dauert, eine Transaktion zu begehen. In Ihrem ersten Beispiel benötigte jede Transaktion etwa 0,10, was ziemlich nahe an der Transaktionszeit für das Einfügen von 10 Datensätzen liegt. Welche Art von Ergebnissen erhalten Sie, wenn Sie 100 oder 1000 Aktualisierungen in einer einzigen Transaktion durchführen?

Auch SQLite erwartet etwa 60 Transaktionen pro Sekunde auf einer durchschnittlichen Festplatte, während Sie nur etwa 10 erhalten. Könnte Ihre Festplattenleistung hier das Problem sein?

https://sqlite.org/faq.html#q19

+0

Bin ich wegen der Festplattengeschwindigkeit und des synchronen Modus begrenzt, der auf SQLite wartet, um die geschriebenen Daten zu überprüfen? Also, wenn ich Sicherheit über die Leistung wähle, bin ich auf ~ 10 ms pro Update-Abfrage mit einer typischen 7200-Festplatte beschränkt? Ich habe nicht mit 100 oder 1000 Updates getestet, weil meine Anwendung nur maximal 15-20 Abfragen pro Transaktion behandelt (mit einigen SELECTs dazwischen), also habe ich das Szenario in der Frage nachgemacht. Ich habe meine Platte vor einer Woche zerlegt, ich mache es wieder und antworte :) – user6096479

+0

Nein, die Zeiten reichen immer noch von 0,10 bis 0,14 Sekunden bei einer Transaktion von 10 Abfragen. – user6096479

+0

siehe meine Frage update – user6096479

0

Try Indizes zu Ihrer Datenbank hinzufügen:

CREATE INDEX IDXname ON player (name)