Entwicklungshandbuch

Local Navigation

Leistung von SQLite-Datenbanken

Im Vergleich zu Computern stellen mobile Geräte eine sehr eingeschränkte Umgebung für SQLite-Datenbanken bereit. Um eine optimale Leistung auf einem BlackBerry-Smartphone zu erzielen, berücksichtigen Sie die folgenden Einschränkungen:

  • Der dynamische Speicher ist auf 16 MB beschränkt. Diese Beschränkung bezieht sich auf die Größe des RAM, der für eine SQLite-Datenbank zum Speichern interner Datenstrukturen für Schemas und Transaktionen verfügbar ist. Das gesamte Datenbankschema wird in den Speicher geladen, wenn eine SQLite-Datenbank geöffnet wird. Es bleibt erhalten, bis die Datenbank geschlossen wird.

  • Die maximale SQL-Anfragenlänge beträgt 1 MB.

  • Die maximale Zahl von Datenbanken, der gleichzeitig geöffnet sein können, beträgt ungefähr 50. Die Datenbank-API verwendet einen Modus für einen gemeinsam genutzten Cache, um den Speicher bei mehreren Verbindungen zur selben Datenbank effizient zu nutzen.

  • Es kann immer nur eine Lese-/Schreibverbindung zu einer SQLite-Datenbank hergestellt werden. Andere Datenbankverbindungen sind schreibgeschützt.

Wenn Sie die Methoden Database.createBlobOutputStream und Database.createBlobInputStream zum Lesen und Schreiben von BLOBs verwenden, schränkt der für SQLite verfügbare Speicher nicht die Größe des BLOB ein, das Sie verwenden können: Ihr Ergebnissatz ist nicht auf 1 MB beschränkt.

Sie können die Speicherauslastung für jede Datenbankverbindung mithilfe der folgenden Methoden nachverfolgen: Database.getCacheUsed, Database.getSchemaUsed und Database.getStatementUsed.

SQLite als Dienst

Auf einem BlackBerry-Smartphone wird SQLite als Dienst ausgeführt. Datenbankoperationen verwenden eine Laufzeitbrücke, um Daten zwischen Java und systemeigenem Code zu übertragen. Die Datenmenge, die über die Laufzeitbrücke übertragen werden kann, ist auf 1 MB beschränkt. Die Laufzeitbrücke kann eine hervorragende Leistung von Datenbankoperationen ermöglichen, aber sie kann die Leistung auch verringern, wenn sie nicht richtig verwendet wird.

Wenn Sie eine vorbereitete Anweisung zum Einfügen oder Aktualisieren von Daten erstellen, sollten Sie die Methode Statement.executeInsert oder Statement.executeUpdate verwenden, um die Anzahl von Aufrufen über die Laufzeitbrücke zu verringern. Diese Methoden führen die folgenden Operationen in systemeigenem Code aus:

  • Binden von SQL-Parametern
  • Ausführen der Anweisung
  • Zurücksetzen der Anweisung
  • Löschen der Bindungen
  • Zurückgeben der zuletzt eingefügten Zeilen-ID (nur executeInsert)

Wenn Sie eine Abfrage ausführen, die keinen Ergebnissatz zurückgibt, und keine Parameter binden, sollten Sie die Database.executeStatement-Methode verwenden, die die Anzahl von Aufrufen über die Laufzeitbrücke durch Ausführen der folgenden Operationen in systemeigenem Code verringert:

  • Vorbereiten der Anweisung
  • Ausführen der Anweisung
  • Abschließen der Anweisung

Wenn Sie eine Abfrage, die Ergebnissätze zurückgibt, mithilfe der Statement.getCursor-Methode ausführen, können Sie mithilfe der Statement.setCursorBufferSize-Methode im Voraus eine festgelegte Anzahl von Zeilen abrufen. Dieser Ansatz verringert die Verwendung der Laufzeitbrücke. Wenn sich der Cursor über den gepufferten Satz hinaus bewegt, wird automatisch ein neuer Zeilenstapel abgerufen. Mit der Statement.getCursorBufferSize-Methode können Sie die Anzahl der Zeilen abrufen, die ein Cursor puffert.

Wenn Sie Ganzzahlwerte abrufen, die als Schlüssel in einer anderen Abfrage verwendet werden sollen, können Sie die Methoden Statement.getIntegers und Statement.getLongs verwenden. Diese Methoden vereinfachen und optimieren das Abrufen von Ganzzahlspalten.

Bewährtes Verfahren: Optimieren der Leistung von SQLite-Datenbanken

Befolgen Sie die folgenden Anweisungen:

Bewährtes Verfahren Beschreibung

Verwenden Sie den geeigneten Journal-Modus.

Standardmäßig wird ein Rollback-Journal verwendet, um Transaktionen aufzuzeichnen, aber Sie können stattdessen auch ein Write-Ahead-Protokoll verwenden. Das Write-Ahead-Protokoll bietet eine verbesserte Gleichzeitigkeit und ist in den meisten Situationen schneller als das Rollback-Journal, weil das Schreiben in der Datenbank nicht das Lesen blockiert. Allerdings verwendet das Write-Ahead-Protokoll mehr Datei-Handles. Das Write-Ahead-Protokoll verwendet bis zu drei Datei-Handles, die für lange Zeit beibehalten werden können, während das Rollback-Journal bei Schreibtransaktionen eines oder gelegentlich zwei verwendet. Um den Journal-Modus in ein Write-Ahead-Protokoll zu ändern, führen Sie die SQL-Anweisung "PRAGMA journal_mode = WAL;" aus.

Erwägen Sie das Lockern von Sicherheitsbeschränkungen für Tabellen.

Wenn die Datenintegrität kein Problem ist, erwägen Sie, Pragma-Befehle zu verwenden, um kein Journal festzulegen und den synchronen Modus zu deaktivieren. Beispiel: PRAGMA journal_mode=OFF und PRAGMA synchronous=OFF.

Test

Wenn Sie planen, eine Datenbank mit einem großen Schema zu erstellen oder große BLOBs einzufügen, sollten Sie die Datenbank auf Ihren BlackBerry-Zielsmartphones testen, um sicherzustellen, dass die Smartphones über ausreichend Speicher verfügen.

Speichern Sie so wenige Daten wie möglich.

Der größte Teil der Verarbeitungszeit von SQLite-Datenbanken wird zum Lesen vom und Schreiben in den Speicher aufgewendet. Weniger Daten bedeuten im Allgemeinen, dass weniger Lese- und Schreibvorgänge durchgeführt werden. Die SQLite-Datenbank-Engine legt häufig aufgerufene Datenbankseiten im Cache-Speicher ab. Wenn Sie weniger Daten speichern, können Sie die Wahrscheinlichkeit erhöhen, dass die SQLite-Bibliothek angeforderte Daten schneller aus dem Cache und nicht aus dem relativ langsamen Flash-Speicher abruft.

Gruppieren Sie Anweisungen mit expliziten Transaktionen.

Wenn Sie keine expliziten Transaktionen verwenden, wird für jede Anweisung, die Sie ausführen, eine Transaktion erstellt. Dieses Standardverhalten ist nicht effizient. Hierzu ist es erforderlich, die Journaldatei für jede Anweisung zu öffnen, erneut zu öffnen, in die Datei zu schreiben und sie wieder zu schließen. Bei Verwendung expliziter Transaktionen können Sie Anweisungen gruppieren, um die Effizienz zu steigern. Sie können mehrere Anweisungen in einer Transaktion ausführen, indem Sie Database.beginTransaction und Database.commitTransaction um eine Gruppe von Anweisungen herum platzieren.

Erstellen Sie effiziente Indizes.

Indizes können die erforderliche Zeit zum Scannen von Tabellen erheblich verringern. Hier sind einige Hinweise zum Erstellen effizienter Indizes:

  • Mit Indizes lässt sich die Leistung für schreibgeschützte Abfragen wie z. B. SELECT verbessern, aber sie können die Leistung für Abfragen verringern, die Zeilen ändern. Erwägen Sie, nur jene Tabellen zu indizieren, die selten aktualisiert werden.
  • Die Reihenfolge von Spalten in einem Index beeinflusst die Leistung. Spalten, die in der Regel in WHERE-Klauseln verwendet werden, sollten vorangestellt werden, gefolgt von Spalten, die in der Regel in ORDER BY-Klauseln verwendet werden.
  • Erstellen Sie für Spalten mit abzurufenden Daten einen abdeckenden Index.
  • Vermeiden Sie doppelte Indizes. Die SQLite-Datenbank-Engine erstellt automatisch Indizes für Spalten, die die Einschränkung UNIQUE oder PRIMARY KEY aufweisen.
  • Spalten, die als INTEGER PRIMARY KEY deklariert sind, ermöglichen schnellere Abfragen, weil sie direkt auf Tabellendaten zugreifen können.

Minimieren Sie die Größe von Reihen.

Wenn Sie eine sehr breite Spalte haben, sollten Sie in Betracht ziehen, sie in einer separaten Tabelle abzulegen.

Speichern Sie BLOBs auf geeignete Weise.

Wenn Ihre Daten BLOBs enthalten, sollten Sie sie mithilfe der Objekte InputStream und OutputStream sowie mittels createBlobInputStream und createBlobOutputStream lesen und schreiben.

Sie können auch erwägen, jedes BLOB in einer separaten Tabelle zu speichern. Wenn die BLOBs sehr groß sind, können Sie sie als Dateien außerhalb der Datenbank speichern (und den Pfad zu jeder Datei in der Datenbank speichern), allerdings führt diese Methode zu einer hohen Anzahl von Dateinamen-Suchen.

Erwägen Sie die Verwendung temporärer Tabellen.

Wenn Sie die Daten nach einem Neustart eines Smartphones nicht mehr benötigen, verwenden Sie die Anweisung CREATE TEMP TABLE statt der Anweisung CREATE TABLE.

Verwenden Sie SQL Parameter.

Um einen Satz von Anweisungen desselben Formats auszuführen, bereiten Sie zuerst eine generische Anweisung vor, die SQL-Parameter verwendet. Sie können die Anweisung durch Iterationen durch die variablen Werte und Anbinden der Werte an die genannten Variablen in jeder Iteration ausführen.

Vermeiden Sie Unterabfragen.

In einigen Fällen speichert die SQLite-Datenbank-Engine Ergebnisse von Unterabfragen in einer temporären Datei, was die Verarbeitung verlangsamen kann.

Defragmentieren Sie die Datenbank

Verwenden Sie den SQLite-VACUUM-Befehl, um die Datenbank zu defragmentieren. Dieser Prozess verringert auch die Größe der Datenbankdatei.

Ziehen Sie die Reihenfolge der Spalten in Tabellendeklarationen in Betracht.

Die Reihenfolge von Spalten in einer Tabellendeklaration beeinflusst die Leistung, insbesondere in der Abwesenheit eines Indexes, weil die SQLite-Datenbank-Engine die Spalten in der Reihenfolge scannt, die in der Tabellendeklaration definiert wird. Spalten, die wenige Daten enthalten, auf die oft zugegriffen wird, sollten vor Spalten platziert werden, die viele Daten enthalten, auf die selten zugegriffen wird.

Verwenden Sie Massenvorgangsmethoden.

Die folgenden Massenvorgangsmethoden nutzen die Laufzeitbrücke auf effiziente Weise:

Weitere Informationen finden Sie unter SQLite als Dienst.


Waren diese Informationen hilfreich? Senden Sie uns Ihren Kommentar.