Android bietet integrierte Unterstützung für SQLite, eine effiziente SQL-Datenbank. Beachten Sie diese Best Practices, um die Leistung Ihrer App zu optimieren und dafür zu sorgen, dass sie auch bei steigenden Datenmengen schnell und zuverlässig bleibt. Mit diesen Best Practices verringern Sie auch die Wahrscheinlichkeit, Es stößt auf Leistungsprobleme, die sich nur schwer reproduzieren lassen, zur Fehlerbehebung.
Beachten Sie die folgenden Leistungsprinzipien, um eine höhere Leistung zu erzielen:
Weniger Zeilen und Spalten anzeigen: Optimieren Sie Ihre Abfragen, um nur die notwendige Daten. Minimieren Sie die aus der Datenbank gelesene Datenmenge, kann sich ein übermäßiger Datenabruf negativ auf die Leistung auswirken.
Arbeit an die SQLite-Engine übergeben: Berechnungen, Filter- und Sortiervorgänge in den SQL-Abfragen ausführen. Die Verwendung der Abfrage-Engine von SQLite kann erheblich die Leistung zu verbessern.
Datenbankschema ändern: Datenbankschema zur Unterstützung von SQLite entwerfen effiziente Abfragepläne und Datendarstellungen zu erstellen. Tabellen richtig indexieren und optimieren Sie Tabellenstrukturen, um die Leistung zu verbessern.
Außerdem können Sie mit den verfügbaren Tools zur Fehlerbehebung SQLite-Datenbank, um Bereiche zu identifizieren, Optimierung.
Wir empfehlen die Verwendung der Jetpack Room-Bibliothek.
Datenbank für Leistung konfigurieren
Führen Sie die Schritte in diesem Abschnitt aus, um Ihre Datenbank für eine optimale die Performance in SQLite.
Write-Ahead-Logging aktivieren
SQLite implementiert Mutationen durch Anhängen an ein Log, was gelegentlich in die Datenbank komprimiert. Dies wird als Write-Ahead-Logging (WAL).
Aktivieren
WAL
außer Sie verwenden ATTACH
DATABASE
.
Synchronisierungsmodus lockern
Bei der Verwendung von WAL wird standardmäßig bei jedem Commit eine fsync
ausgegeben, um sicherzustellen, dass die Daten das Laufwerk erreichen. Dies verbessert die Langlebigkeit der Daten, verlangsamt aber
Commits übergeben.
SQLite bietet eine Option, um synchrone
Modus an. Wenn Sie WAL aktivieren, legen Sie den synchronen Modus auf NORMAL
fest:
Kotlin
db.execSQL("PRAGMA synchronous = NORMAL")
Java
db.execSQL("PRAGMA synchronous = NORMAL");
Bei dieser Einstellung kann ein Commit zurückgegeben werden, bevor die Daten auf einem Laufwerk gespeichert sind. Wenn ein wenn das Gerät heruntergefahren wird, beispielsweise bei Stromausfall oder bei einer Kernel-Panic, übergebene Daten verloren gehen. Aufgrund des Loggings wird Ihre Datenbank korrumpiert.
Wenn nur Ihre App abstürzt, gelangen Ihre Daten trotzdem auf das Laufwerk. Bei den meisten Apps ohne materielle Kosten Leistungsverbesserungen zu erzielen.
Effiziente Tabellenschemas definieren
Definieren Sie ein effizientes Tabellenschema, um die Leistung zu optimieren und den Datenverbrauch zu minimieren. SQLite erstellt effiziente Abfragepläne und Daten, was zu schnelleren Datenabruf. Dieser Abschnitt enthält Best Practices zum Erstellen einer Tabelle Schemas.
INTEGER PRIMARY KEY
Definieren und füllen Sie für dieses Beispiel eine Tabelle so aus:
CREATE TABLE Customers(
id INTEGER,
name TEXT,
city TEXT
);
INSERT INTO Customers Values(456, 'John Lennon', 'Liverpool, England');
INSERT INTO Customers Values(123, 'Michael Jackson', 'Gary, IN');
INSERT INTO Customers Values(789, 'Dolly Parton', 'Sevier County, TN');
Die Tabellenausgabe sieht so aus:
Rowid (Zeilen-ID) | id | Name | Stadt |
---|---|---|---|
1 | 456 | John Lennon | Liverpool, England |
2 | 123 | Michael Jackson | Gary, IN |
3 | 789 | Dolly Parton | Sevier County, Tennessee |
Die Spalte rowid
ist
Index, der den Anzeigenauftrag beibehält. Suchanfragen, die
nach rowid
filtern, werden als schnelle B-Baum-Suche implementiert. Suchanfragen, die
Nach id
filtern ist ein langsamer Tabellenscan.
Wenn Sie Suchvorgänge bis zum id
durchführen möchten, können Sie das Speichern des
rowid
-Spalte für weniger Daten im Speicher und eine
schnellere Datenbank:
CREATE TABLE Customers(
id INTEGER PRIMARY KEY,
name TEXT,
city TEXT
);
Ihre Tabelle sieht jetzt so aus:
id | Name | Stadt |
---|---|---|
123 | Michael Jackson | Gary, IN |
456 | John Lennon | Liverpool, England |
789 | Dolly Parton | Sevier County, Tennessee |
Da Sie die Spalte rowid
nicht speichern müssen, sind id
-Abfragen schnell. Hinweis
, dass die Tabelle jetzt basierend auf id
und nicht nach dem Anzeigenauftrag sortiert ist.
Abfragen mithilfe von Indexen beschleunigen
SQLite nutzt
Indexe
um Abfragen zu beschleunigen. Beim Filtern (WHERE
), Sortieren (ORDER BY
) oder
Beim Aggregieren (GROUP BY
) einer Spalte, wenn die Tabelle einen Index für die Spalte hat,
Abfrage beschleunigt wird.
Im vorherigen Beispiel muss zum Filtern nach city
die gesamte Tabelle gescannt werden:
SELECT id, name
WHERE city = 'London, England';
Bei einer Anwendung mit vielen Suchanfragen nach Städten können Sie diese Abfragen mit einem Index:
CREATE INDEX city_index ON Customers(city);
Ein Index wird als zusätzliche Tabelle implementiert, sortiert nach der Indexspalte
zugeordnet zu rowid
:
Stadt | Rowid (Zeilen-ID) |
---|---|
Gary, IN | 2 |
Liverpool, England | 1 |
Sevier County, Tennessee | 3 |
Die Speicherkosten der Spalte city
sind jetzt doppelt so hoch,
die jetzt sowohl in der Originaltabelle
als auch im Index vorhanden sind. Da Sie die Methode
Index ist, sind die Kosten für zusätzlichen Speicher den Nutzen schnellerer Abfragen wert.
Pflegen Sie jedoch keinen Index, den Sie nicht nutzen,
wenn keine Steigerung
der Abfrageleistung erforderlich ist.
Mehrspaltige Indexe erstellen
Wenn Sie in Ihren Abfragen mehrere Spalten kombinieren, können Sie Indexe mit mehreren Spalten erstellen, um die Abfrage zu beschleunigen. Sie können einen Index auch für eine externe Spalte verwenden und lassen Sie die Suche innerhalb als linearen Scan durchführen.
Nehmen wir zum Beispiel die folgende Abfrage:
SELECT id, name
WHERE city = 'London, England'
ORDER BY city, name
Sie können die Abfrage mit einem mehrspaltigen Index in der gleichen Reihenfolge wie in der Abfrage angegeben:
CREATE INDEX city_name_index ON Customers(city, name);
Wenn Sie jedoch nur einen Index für city
haben, wird die äußere Sortierung trotzdem beschleunigt, während für die innere Sortierung ein linearer Scan erforderlich ist.
Das funktioniert auch bei Suchanfragen mit Präfix. Beispiel: Ein Index
ON Customers (city, name)
beschleunigt auch das Filtern, Sortieren und Gruppieren.
nach city
, da die Indextabelle für einen mehrspaltigen Index nach dem
in der angegebenen Reihenfolge
angezeigt werden sollen.
WITHOUT ROWID
in Betracht ziehen
Standardmäßig erstellt SQLite eine rowid
-Spalte für Ihre Tabelle, wobei rowid
ein
implizites INTEGER PRIMARY KEY AUTOINCREMENT
Wenn Sie bereits eine Spalte mit dem Namen INTEGER PRIMARY KEY
haben, wird diese Spalte zu einem Alias von rowid
.
Für Tabellen mit einem anderen Primärschlüssel als INTEGER
oder einem zusammengesetzten Primärschlüssel können Sie WITHOUT
ROWID
verwenden.
Kleine Daten als BLOB
und große Daten als Datei speichern
Wenn Sie einer Zeile große Daten zuordnen möchten, z. B. ein Thumbnail eines Bildes oder ein Foto für einen Kontakt, können Sie die Daten entweder in einer BLOB
-Spalte oder in einer Datei speichern und dann den Dateipfad in der Spalte speichern.
Dateien werden in der Regel in Schritten von 4 KB aufgerundet. Bei sehr kleinen Dateien,
der Rundungsfehler erheblich ist, ist es effizienter, sie in der
Datenbank als BLOB
. SQLite minimiert Dateisystemaufrufe und ist schneller als die
zugrunde liegendes Dateisystem
.
Abfrageleistung verbessern
Befolgen Sie diese Best Practices, um die Abfrageleistung in SQLite zu verbessern, indem Sie Reaktionszeiten und maximale Verarbeitungseffizienz.
Nur die benötigten Zeilen lesen
Mit Filtern können Sie die Ergebnisse eingrenzen, indem Sie bestimmte Kriterien, wie Zeitraum, Standort oder Name. Mit Limits können Sie die Anzahl der der angezeigten Ergebnisse:
Kotlin
db.rawQuery(""" SELECT name FROM Customers LIMIT 10; """.trimIndent(), null ).use { cursor -> while (cursor.moveToNext()) { ... } }
Java
try (Cursor cursor = db.rawQuery(""" SELECT name FROM Customers LIMIT 10; """, null)) { while (cursor.moveToNext()) { ... } }
Nur die benötigten Spalten lesen
Vermeiden Sie die Auswahl nicht benötigter Spalten, Abfragen verlangsamen und Ressourcen verschwenden. Wählen Sie stattdessen nur Spalten aus, die verwendet werden.
Im folgenden Beispiel wählen Sie id
, name
und phone
aus:
Kotlin
// This is not the most efficient way of doing this. // See the following example for a better approach. db.rawQuery( """ SELECT id, name, phone FROM customers; """.trimIndent(), null ).use { cursor -> while (cursor.moveToNext()) { val name = cursor.getString(1) // ... } }
Java
// This is not the most efficient way of doing this. // See the following example for a better approach. try (Cursor cursor = db.rawQuery(""" SELECT id, name, phone FROM customers; """, null)) { while (cursor.moveToNext()) { String name = cursor.getString(1); ... } }
Sie benötigen jedoch nur die Spalte name
:
Kotlin
db.rawQuery(""" SELECT name FROM Customers; """.trimIndent(), null ).use { cursor -> while (cursor.moveToNext()) { val name = cursor.getString(0) ... } }
Java
try (Cursor cursor = db.rawQuery(""" SELECT name FROM Customers; """, null)) { while (cursor.moveToNext()) { String name = cursor.getString(0); ... } }
Abfragen mit SQL-Karten und nicht mit Stringkonkatenierung parametrisieren
Ihr Abfragestring kann einen Parameter enthalten, der erst zur Laufzeit bekannt ist, z. B.:
Kotlin
fun getNameById(id: Long): String? db.rawQuery( "SELECT name FROM customers WHERE id=$id", null ).use { cursor -> return if (cursor.moveToFirst()) { cursor.getString(0) } else { null } } }
Java
@Nullable public String getNameById(long id) { try (Cursor cursor = db.rawQuery( "SELECT name FROM customers WHERE id=" + id, null)) { if (cursor.moveToFirst()) { return cursor.getString(0); } else { return null; } } }
Im vorhergehenden Code konstruiert jede Abfrage einen anderen String.
hat keinen Nutzen
von dem Anweisungs-Cache. Für jeden Aufruf muss SQLite kompilieren
bevor sie ausgeführt werden kann. Stattdessen können Sie das Argument id
durch ein
parameter und
Binden Sie den Wert mit selectionArgs
:
Kotlin
fun getNameById(id: Long): String? { db.rawQuery( """ SELECT name FROM customers WHERE id=? """.trimIndent(), arrayOf(id.toString()) ).use { cursor -> return if (cursor.moveToFirst()) { cursor.getString(0) } else { null } } }
Java
@Nullable public String getNameById(long id) { try (Cursor cursor = db.rawQuery(""" SELECT name FROM customers WHERE id=? """, new String[] {String.valueOf(id)})) { if (cursor.moveToFirst()) { return cursor.getString(0); } else { return null; } } }
Jetzt kann die Abfrage einmal kompiliert und im Cache gespeichert werden. Die kompilierte Abfrage wird wiederverwendet
zwischen verschiedenen Aufrufen von getNameById(long)
.
In SQL iterieren, nicht im Code
Verwenden Sie statt einer programmatischen Abfrage eine einzelne Abfrage, die alle Zielergebnisse zurückgibt SQL-Abfragen iterieren, um einzelne Ergebnisse zurückzugeben. Das Programmatik ist etwa 1.000-mal langsamer als eine einzelne SQL-Abfrage.
DISTINCT
für eindeutige Werte verwenden
Mit dem Keyword DISTINCT
lässt sich die Leistung Ihrer Abfragen verbessern, da die Menge der zu verarbeitenden Daten reduziert wird. Wenn Sie zum Beispiel
Um nur die eindeutigen Werte aus einer Spalte zurückzugeben, verwenden Sie DISTINCT
:
Kotlin
db.rawQuery(""" SELECT DISTINCT name FROM Customers; """.trimIndent(), null ).use { cursor -> while (cursor.moveToNext()) { // Only iterate over distinct names in Kotlin ... } }
Java
try (Cursor cursor = db.rawQuery(""" SELECT DISTINCT name FROM Customers; """, null)) { while (cursor.moveToNext()) { // Only iterate over distinct names in Java ... } }
Nach Möglichkeit Aggregatfunktionen verwenden
Verwenden Sie Aggregatfunktionen für aggregierte Ergebnisse ohne Zeilendaten. Beispiel: Der Parameter Der folgende Code prüft, ob mindestens eine übereinstimmende Zeile vorhanden ist:
Kotlin
// This is not the most efficient way of doing this. // See the following example for a better approach. db.rawQuery(""" SELECT id, name FROM Customers WHERE city = 'Paris'; """.trimIndent(), null ).use { cursor -> if (cursor.moveToFirst()) { // At least one customer from Paris ... } else { // No customers from Paris ... }
Java
// This is not the most efficient way of doing this. // See the following example for a better approach. try (Cursor cursor = db.rawQuery(""" SELECT id, name FROM Customers WHERE city = 'Paris'; """, null)) { if (cursor.moveToFirst()) { // At least one customer from Paris ... } else { // No customers from Paris ... } }
Um nur die erste Zeile abzurufen, können Sie EXISTS()
verwenden, um 0
zurückzugeben, wenn eine Übereinstimmung
Zeile nicht vorhanden und 1
, wenn eine oder mehrere Zeilen übereinstimmen:
Kotlin
db.rawQuery(""" SELECT EXISTS ( SELECT null FROM Customers WHERE city = 'Paris'; ); """.trimIndent(), null ).use { cursor -> if (cursor.moveToFirst() && cursor.getInt(0) == 1) { // At least one customer from Paris ... } else { // No customers from Paris ... } }
Java
try (Cursor cursor = db.rawQuery(""" SELECT EXISTS ( SELECT null FROM Customers WHERE city = 'Paris' ); """, null)) { if (cursor.moveToFirst() && cursor.getInt(0) == 1) { // At least one customer from Paris ... } else { // No customers from Paris ... } }
SQLite-Aggregat verwenden Funktionen in Ihrer App Code:
COUNT
: zählt, wie viele Zeilen sich in einer Spalte befinden.SUM
: Addiert alle numerischen Werte in einer Spalte.MIN
oderMAX
: Hiermit wird der niedrigste oder höchste Wert festgelegt. Funktioniert für numerische SpaltenDATE
- und Texttypen.AVG
: Ermittelt den numerischen durchschnittlichen Wert.GROUP_CONCAT
: verkettet Strings mit einem optionalen Trennzeichen.
COUNT()
statt Cursor.getCount()
verwenden
Im
im folgenden Beispiel
Cursor.getCount()
-Funktion
liest alle Zeilen aus der Datenbank und gibt alle Zeilenwerte zurück:
Kotlin
// This is not the most efficient way of doing this. // See the following example for a better approach. db.rawQuery(""" SELECT id FROM Customers; """.trimIndent(), null ).use { cursor -> val count = cursor.getCount() }
Java
// This is not the most efficient way of doing this. // See the following example for a better approach. try (Cursor cursor = db.rawQuery(""" SELECT id FROM Customers; """, null)) { int count = cursor.getCount(); ... }
Wenn Sie jedoch COUNT()
verwenden, gibt die Datenbank nur die Anzahl zurück:
Kotlin
db.rawQuery(""" SELECT COUNT(*) FROM Customers; """.trimIndent(), null ).use { cursor -> cursor.moveToFirst() val count = cursor.getInt(0) }
Java
try (Cursor cursor = db.rawQuery(""" SELECT COUNT(*) FROM Customers; """, null)) { cursor.moveToFirst(); int count = cursor.getInt(0); ... }
Nest-Abfragen anstelle von Code
SQL ist zusammensetzbar und unterstützt Unterabfragen, Joins und Fremdschlüsseleinschränkungen. Sie können das Ergebnis einer Abfrage in einer anderen Abfrage verwenden, ohne die App Code. Dadurch müssen weniger Daten aus SQLite kopiert werden und die Datenbank-Engine kann Ihre Abfrage optimieren.
Im folgenden Beispiel können Sie eine Abfrage ausführen, um zu ermitteln, in welcher Stadt Kundschaft finden, verwenden Sie dann das Ergebnis einer anderen Abfrage, um alle Kunden diese Stadt:
Kotlin
// This is not the most efficient way of doing this. // See the following example for a better approach. db.rawQuery(""" SELECT city FROM Customers GROUP BY city ORDER BY COUNT(*) DESC LIMIT 1; """.trimIndent(), null ).use { cursor -> if (cursor.moveToFirst()) { val topCity = cursor.getString(0) db.rawQuery(""" SELECT name, city FROM Customers WHERE city = ?; """.trimIndent(), arrayOf(topCity)).use { innerCursor -> while (innerCursor.moveToNext()) { ... } } } }
Java
// This is not the most efficient way of doing this. // See the following example for a better approach. try (Cursor cursor = db.rawQuery(""" SELECT city FROM Customers GROUP BY city ORDER BY COUNT(*) DESC LIMIT 1; """, null)) { if (cursor.moveToFirst()) { String topCity = cursor.getString(0); try (Cursor innerCursor = db.rawQuery(""" SELECT name, city FROM Customers WHERE city = ?; """, new String[] {topCity})) { while (innerCursor.moveToNext()) { ... } } } }
Um das Ergebnis in der Hälfte der Zeit des vorherigen Beispiels zu erhalten, verwenden Sie ein einzelnes SQL mit verschachtelten Anweisungen:
Kotlin
db.rawQuery(""" SELECT name, city FROM Customers WHERE city IN ( SELECT city FROM Customers GROUP BY city ORDER BY COUNT (*) DESC LIMIT 1; ); """.trimIndent(), null ).use { cursor -> if (cursor.moveToNext()) { ... } }
Java
try (Cursor cursor = db.rawQuery(""" SELECT name, city FROM Customers WHERE city IN ( SELECT city FROM Customers GROUP BY city ORDER BY COUNT(*) DESC LIMIT 1 ); """, null)) { while(cursor.moveToNext()) { ... } }
Eindeutigkeit in SQL prüfen
Wenn eine Zeile nur eingefügt werden darf, wenn ein bestimmter Spaltenwert im ist es möglicherweise effizienter, diese Eindeutigkeit als Spalte Einschränkung.
Im folgenden Beispiel wird eine Abfrage ausgeführt, um zu validieren, dass die Zeile und eine weitere zum Einfügen:
Kotlin
// This is not the most efficient way of doing this. // See the following example for a better approach. db.rawQuery( """ SELECT EXISTS ( SELECT null FROM customers WHERE username = ? ); """.trimIndent(), arrayOf(customer.username) ).use { cursor -> if (cursor.moveToFirst() && cursor.getInt(0) == 1) { throw AddCustomerException(customer) } } db.execSQL( "INSERT INTO customers VALUES (?, ?, ?)", arrayOf( customer.id.toString(), customer.name, customer.username ) )
Java
// This is not the most efficient way of doing this. // See the following example for a better approach. try (Cursor cursor = db.rawQuery(""" SELECT EXISTS ( SELECT null FROM customers WHERE username = ? ); """, new String[] { customer.username })) { if (cursor.moveToFirst() && cursor.getInt(0) == 1) { throw new AddCustomerException(customer); } } db.execSQL( "INSERT INTO customers VALUES (?, ?, ?)", new String[] { String.valueOf(customer.id), customer.name, customer.username, });
Anstatt die eindeutige Einschränkung in Kotlin oder Java zu prüfen, können Sie sie in SQL beim Definieren der Tabelle:
CREATE TABLE Customers(
id INTEGER PRIMARY KEY,
name TEXT,
username TEXT UNIQUE
);
SQLite tut dasselbe wie:
CREATE TABLE Customers(...);
CREATE UNIQUE INDEX CustomersUsername ON Customers(username);
Jetzt können Sie eine Zeile einfügen und SQLite die Einschränkung prüfen lassen:
Kotlin
try { db.execSql( "INSERT INTO Customers VALUES (?, ?, ?)", arrayOf(customer.id.toString(), customer.name, customer.username) ) } catch(e: SQLiteConstraintException) { throw AddCustomerException(customer, e) }
Java
try { db.execSQL( "INSERT INTO Customers VALUES (?, ?, ?)", new String[] { String.valueOf(customer.id), customer.name, customer.username, }); } catch (SQLiteConstraintException e) { throw new AddCustomerException(customer, e); }
SQLite unterstützt eindeutige Indexe mit mehreren Spalten:
CREATE TABLE table(...);
CREATE UNIQUE INDEX unique_table ON table(column1, column2, ...);
SQLite validiert Einschränkungen schneller und mit weniger Aufwand als Kotlin oder Java Code. Es empfiehlt sich, anstelle von App-Code SQLite zu verwenden.
Mehrere Einfügungen in einer einzigen Transaktion im Batch ausführen
Mit einer Transaktion werden mehrere Vorgänge verbindlich ausgeführt, was nicht nur die Effizienz, sondern auch die Korrektheit verbessert. Um die Datenkonsistenz und und die Leistung zu beschleunigen, können Sie das Einfügen im Batch tun:
Kotlin
db.beginTransaction() try { customers.forEach { customer -> db.execSql( "INSERT INTO Customers VALUES (?, ?, ...)", arrayOf(customer.id.toString(), customer.name, ...) ) } } finally { db.endTransaction() }
Java
db.beginTransaction(); try { for (customer : Customers) { db.execSQL( "INSERT INTO Customers VALUES (?, ?, ...)", new String[] { String.valueOf(customer.id), customer.name, ... }); } } finally { db.endTransaction() }
Tools zur Fehlerbehebung verwenden
SQLite bietet die folgenden Tools zur Fehlerbehebung, um die Leistung zu messen.
Interaktiven Prompt von SQLite verwenden
SQLite auf Ihrem Computer ausführen, um Abfragen auszuführen und Neues zu lernen
Verschiedene Android-Plattformversionen verwenden unterschiedliche Versionen von SQLite. Wenn Sie dieselbe Engine wie auf einem Android-Gerät verwenden möchten, verwenden Sie adb shell
und führen Sie sqlite3
auf dem Zielgerät aus.
Sie können SQLite bitten, Abfragen zu zeitlich zu erfassen:
sqlite> .timer on
sqlite> SELECT ...
Run Time: real ... user ... sys ...
EXPLAIN QUERY PLAN
Sie können SQLite bitten, zu erklären, wie eine Abfrage beantwortet werden soll, indem Sie die
EXPLAIN QUERY PLAN
:
sqlite> EXPLAIN QUERY PLAN
SELECT id, name
FROM Customers
WHERE city = 'Paris';
QUERY PLAN
`--SCAN Customers
Im vorherigen Beispiel ist ein vollständiger Tabellenscan ohne Index erforderlich, um alle aus Paris zu finden. Dies wird als lineare Komplexität bezeichnet. SQLite muss lesen alle Zeilen und behalten nur die Zeilen bei, die mit Kunden aus Paris übereinstimmen. Um das Problem zu beheben können Sie einen Index hinzufügen:
sqlite> CREATE INDEX Idx1 ON Customers(city);
sqlite> EXPLAIN QUERY PLAN
SELECT id, name
FROM Customers
WHERE city = 'Paris';
QUERY PLAN
`--SEARCH test USING INDEX Idx1 (city=?
Wenn Sie die interaktive Shell verwenden, können Sie SQLite bitten, Abfragepläne:
sqlite> .eqp on
Weitere Informationen finden Sie unter Abfrageplanung.
SQLite-Analysetool
SQLite bietet
sqlite3_analyzer
Befehlszeile verwenden, um zusätzliche Informationen auszulesen, die für
Probleme mit der Leistung zu beheben. Zur Installation besuche die
SQLite-Downloadseite
Mit adb pull
können Sie eine Datenbankdatei von einem Zielgerät auf Ihr
Workstation für die Analyse:
adb pull /data/data/<app_package_name>/databases/<db_name>.db
SQLite Browser
Sie können auch das GUI-Tool installieren, SQLite Browser auf SQLite Downloadseite.
Android-Logging
Android protokolliert die Ausführungszeiten von SQLite-Abfragen:
# Enable query time logging
$ adb shell setprop log.tag.SQLiteTime VERBOSE
# Disable query time logging
$ adb shell setprop log.tag.SQLiteTime ERROR
```### Perfetto tracing
### Perfetto tracing {:#perfetto-tracing}
When [configuring Perfetto](https://perfetto.dev/docs/concepts/config), you may
add the following to include tracks for individual queries:
```protobuf
data_sources {
config {
name: "linux.ftrace"
ftrace_config {
atrace_categories: "database"
}
}
}
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Benchmarks in Continuous Integration ausführen
- Eingefrorene Frames
- Baseline-Profile ohne MacroBenchmark erstellen und messen