SQLite
 sql >> Datenbank >  >> RDS >> SQLite

SQLite-Leistungsoptimierung

SQLite ist eine beliebte relationale Datenbank, die Sie in Ihre Anwendung einbetten. Mit einer zunehmenden Datenmenge in Ihrer Datenbank müssen Sie die SQLite-Leistungsoptimierung anwenden. Dieser Artikel behandelt Indizes und ihre Fallstricke, die Verwendung des Abfrageplaners, den Journalmodus Write-Ahead-Logging (WAL) und die Erhöhung der Cache-Größe. Außerdem wird erläutert, wie wichtig es ist, die Auswirkungen Ihrer Optimierungen mithilfe automatisierter Tests zu messen.

Einführung

SQLite ist ein beliebtes relationales Datenbanksystem (DB) . Im Gegensatz zu seinen größeren Client-Server-basierten Brüdern wie MySQL kann SQLite als Bibliothek in Ihre Anwendung eingebettet werden . SQLite hat einen sehr ähnlichen Funktionsumfang und kann auch Millionen von Zeilen verarbeiten, vorausgesetzt, Sie kennen einige Tipps und Tricks zur Leistungsoptimierung. Wie die folgenden Abschnitte zeigen werden, gibt es mehr zu wissen über die SQLite-Leistungsoptimierung als nur das Erstellen von Indizes.

Erstellen Sie Indizes, aber mit Vorsicht

Die grundlegende Idee eines Index ist es, das Lesen zu beschleunigen bestimmter Daten , also SELECT Anweisungen mit einem WHERE Klausel. Indizes beschleunigen auch das Sortieren Daten (ORDER BY ) oder JOIN Tabellen. Leider sind Indizes ein zweischneidiges Schwert, da sie zusätzlichen Speicherplatz verbrauchen und die Datenmanipulation verlangsamen (INSERT , UPDATE , DELETE ).

Der allgemeine Rat lautet, so wenige Indizes wie möglich, aber so viele wie nötig zu erstellen . Außerdem machen Indizes nur für größere Sinn Datenbanken mit Tausenden oder Millionen von Zeilen.

Verwenden Sie den Abfrageplaner, um Ihre Abfragen zu analysieren

Die Art und Weise, wie Indizes intern von SQLite verwendet werden, ist dokumentiert, aber nicht sehr einfach zu verstehen. Wie in diesem Artikel weiter ausgeführt, ist es eine gute Idee, eine Abfrage zu analysieren, indem Sie ihr EXPLAIN QUERY PLAN voranstellen . Schauen Sie sich jede Ausgabezeile an, von der es drei grundlegende Varianten gibt:

  • SEARCH table ... Zeilen sind ein gutes Zeichen – SQLite verwendet einen Ihrer Indizes!
  • SCAN table ... USING INDEX ist ein schlechtes Zeichen,
  • SCAN table ... ist noch schlimmer!

Versuchen Sie, SCAN table [using index] zu vermeiden Einträge in der Ausgabe von EXPLAIN QUERY PLAN wann immer möglich, da Sie bei größeren Datenbanken auf Leistungsprobleme stoßen werden. Verwenden Sie EXPLAIN QUERY PLAN um Ihre Indizes iterativ hinzuzufügen oder zu ändern, bis keine SCAN table mehr vorhanden ist Einträge erscheinen.

Optimieren Sie Abfragen, die IS NOT beinhalten

Die Suche nach IS NOT ... ist teuer da SQLite scannen muss alle Zeilen der Tabelle, auch wenn die betroffene Spalte einen Index hat . Indizes sind nur sinnvoll, wenn Sie nach bestimmten Werten suchen, also Vergleiche mit < (kleiner), > (größer) oder = (gleich), aber sie gelten nicht für !=(ungleich).

Ein netter kleiner Trick ist, dass Sie WHERE column != value ersetzen können mit WHERE column > value OR column < value . Dies verwendet den Index der Spalte und wirkt sich effektiv auf alle Zeilen aus, deren Wert nicht gleich value ist . Ebenso ein WHERE stringColumn != '' kann durch WHERE stringColumn > '' ersetzt werden , weil Zeichenfolgen sortierbar sind. Stellen Sie bei der Anwendung dieses Tricks jedoch sicher, dass Sie wissen, wie SQLite mit NULL umgeht Vergleiche. Beispielsweise wertet SQLite NULL > '' aus als FALSE .

Wenn Sie einen solchen Vergleichstrick verwenden, gibt es eine weitere Einschränkung, falls Ihre Abfrage WHERE enthält und ORDER BY , jeweils mit einer anderen Spalte:Dadurch wird die Abfrage wieder ineffizient. Verwenden Sie nach Möglichkeit dasselbe Spalte in WHERE und ORDER BY , oder erstellen Sie einen überdeckenden Index das betrifft sowohl das WHERE und ORDER BY Spalte.

Verbessern Sie die Schreibgeschwindigkeit mit dem Write-Ahead-Log

Das Write-Ahead-Logging (WAL) Der Journalmodus verbessert die Schreib-/Aktualisierungsleistung erheblich , im Vergleich zum standardmäßigen Rollback Journalmodus. Wie hier dokumentiert, gibt es jedoch ein paar Vorbehalte . Beispielsweise ist der WAL-Modus auf bestimmten Betriebssystemen nicht verfügbar. Außerdem gibt es reduzierte Datenkonsistenzgarantien im Falle eines Hardwarefehlers . Lesen Sie unbedingt die ersten paar Seiten, um zu verstehen, was Sie tun.

Ich habe festgestellt, dass der Befehl PRAGMA synchronous = NORMAL bietet eine 3-4x Beschleunigung. Einstellen von journal_mode zu WAL verbessert dann die Performance nochmals deutlich (ca. 10x oder mehr, je nach Betriebssystem).

Abgesehen von den bereits erwähnten Einschränkungen sollten Sie auch Folgendes beachten:

  • Wenn Sie den WAL-Journalmodus verwenden, befinden sich neben der Datenbankdatei in Ihrem Dateisystem zwei zusätzliche Dateien, die den gleichen Namen wie die Datenbank haben, jedoch mit den Endungen „-shm“ und „-wal“. Normalerweise müssen Sie sich nicht darum kümmern, aber wenn Sie die Datenbank an einen anderen Computer senden, während Ihre Anwendung läuft, vergessen Sie nicht, diese beiden Dateien einzuschließen. SQLite wird diese beiden Dateien immer dann in die Hauptdatei komprimieren, wenn Sie normalerweise alle offenen Datenbankverbindungen geschlossen haben.
  • Die Einfüge- oder Aktualisierungsleistung sinkt gelegentlich, wenn die Abfrage das Zusammenführen des Inhalts der WAL-Protokolldatei mit der Hauptdatenbankdatei auslöst. Dies wird als Checkpointing bezeichnet , siehe hier.
  • Ich habe dieses PRAGMA gefunden s, die journal_mode ändern und synchronous scheinen nicht dauerhaft in der Datenbank gespeichert zu sein. Also ich immer führe sie jedes Mal erneut aus, wenn ich eine neue Datenbankverbindung öffne, anstatt sie nur auszuführen, wenn ich die Tabellen zum ersten Mal erstelle.

Alles messen

Wenn Sie Leistungsoptimierungen vornehmen, achten Sie darauf, die Auswirkungen zu messen. Automatisierte (Einheiten-)Tests sind hierfür ein hervorragender Ansatz. Sie helfen beim Dokumentieren Ihre Ergebnisse für Ihr Team, und sie werden im Laufe der Zeit abweichendes Verhalten aufdecken , z.B. wenn Sie auf eine neuere SQLite-Version aktualisieren. Beispiele für das, was Sie messen können:

  • Welchen Effekt hat die Verwendung der WAL Journalmodus über das Rollback Modus? Welche Wirkung haben andere (angeblich) leistungssteigernde PRAGMA s?
  • Wenn Sie einen Index hinzufügen/ändern/entfernen, wie viel schneller SELECT Aussagen werden? Wie viel langsamer ist INSERT/DELETE/UPDATE Aussagen werden?
  • Wie viel zusätzlichen Speicherplatz verbrauchen die Indizes?

Erwägen Sie für jeden dieser Tests, ihn mit unterschiedlichen Datenbankgrößen zu wiederholen. Z.B. Führen Sie sie auf einer leeren Datenbank aus und auch auf einer Datenbank, die bereits Tausende (oder Millionen) von Einträgen enthält. Sie sollten die Tests auch auf verschiedenen Geräten und Betriebssystemen ausführen, insbesondere wenn sich Ihre Entwicklungs- und Produktionsumgebung erheblich unterscheiden.

Stellen Sie die Cache-Größe ein

SQLite speichert temporäre Informationen in einem Cache (im RAM), z.B. beim Erstellen der Ergebnisse eines SELECT Abfrage oder beim Manipulieren von Daten, die noch nicht festgeschrieben wurden. Standardmäßig beträgt diese Größe schlappe 2 MB . Moderne Desktop-Maschinen können viel mehr ersparen. Führen Sie PRAGMA cache_size = -kibibytes aus um diesen Wert zu erhöhen (achten Sie auf das Minus Zeichen vor dem Wert!). Weitere Informationen finden Sie hier. Wieder messen welche Auswirkungen diese Einstellung auf die Leistung hat!

Verwenden Sie REPLACE INTO, um eine Zeile zu erstellen oder zu aktualisieren

Dies ist möglicherweise weniger eine Leistungsoptimierung als vielmehr ein netter kleiner Trick. Angenommen, Sie müssen aktualisieren eine Zeile in der Tabelle t , oder erstellen eine Zeile, falls noch nicht vorhanden. Anstatt zwei Abfragen (SELECT gefolgt von INSERT oder UPDATE ), verwenden Sie den REPLACE INTO (offizielle Dokumente).

Damit dies funktioniert, fügen Sie eine zusätzliche Dummy-Spalte hinzu (z. B. replacer ) in die Tabelle t , die einen UNIQUE hat beschränken. Die Deklaration der Spalte könnte z.B. sei ... replacer INTEGER UNIQUE ... das ist Teil Ihrer CREATE TABLE Erklärung. Verwenden Sie dann eine Abfrage wie

REPLACE INTO t (col1, col2, ..., replacer) VALUES (?,?,...,1)Code language: SQL (Structured Query Language) (sql)

Wenn diese Abfrage zum ersten Mal ausgeführt wird, führt sie einfach ein INSERT aus . Bei der zweiten Ausführung wird der UNIQUE Beschränkung des replacer -Spalte wird ausgelöst, und das Konfliktlösungsverhalten bewirkt, dass die alte Zeile gelöscht und automatisch eine neue erstellt wird. Möglicherweise finden Sie auch den zugehörigen UPSERT-Befehl nützlich.

Schlussfolgerung

Sobald die Anzahl der Zeilen in Ihrer Datenbank wächst, werden Leistungsoptimierungen zu einer Notwendigkeit. Indizes sind die häufigste Lösung. Sie tauschen eine verbesserte Zeitkomplexität gegen eine verringerte Platzkomplexität ein, wodurch die Lesegeschwindigkeit verbessert wird, während die Leistung der Datenänderung negativ beeinflusst wird. A Ich habe gezeigt, dass Sie besonders vorsichtig sein müssen, wenn Sie auf Ungleichheit vergleichen in SELECT -Anweisungen, da SQLite für solche Vergleiche keine Indizes verwenden kann. Ich empfehle generell die Verwendung des Abfrageplaners das erklärt, was intern für jede SQL-Abfrage passiert. Wann immer Sie etwas optimieren, messen Sie die Wirkung!