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 wachsenden Datenmengen schnell und zuverlässig bleibt. Wenn Sie diese Best Practices anwenden, verringern Sie auch die Wahrscheinlichkeit, auf Leistungsprobleme zu stoßen, die schwer zu reproduzieren und zu beheben sind.
Um eine höhere Leistung zu erzielen, beachten Sie die folgenden Leistungsprinzipien:
Weniger Zeilen und Spalten lesen: Optimieren Sie Ihre Abfragen so, dass nur die erforderlichen Daten abgerufen werden. Minimieren Sie die Menge der aus der Datenbank gelesenen Daten, da ein übermäßiges Abrufen von Daten die Leistung beeinträchtigen kann.
Arbeit an die SQLite-Engine übertragen: Führen Sie Berechnungen, Filterungen und Sortierungen in den SQL-Abfragen durch. Die Verwendung der Abfrage-Engine von SQLite kann die Leistung erheblich verbessern.
Datenbankschema ändern: Entwerfen Sie Ihr Datenbankschema so, dass SQLite effiziente Abfragepläne und Datendarstellungen erstellen kann. Indexieren Sie Tabellen richtig und optimieren Sie Tabellenstrukturen, um die Leistung zu verbessern.
Außerdem können Sie die verfügbaren Tools zur Fehlerbehebung verwenden, um die Leistung Ihrer SQLite-Datenbank zu messen und Bereiche zu identifizieren, die optimiert werden müssen.
Wir empfehlen die Verwendung der Jetpack Room-Bibliothek.
Datenbank für Leistung konfigurieren
Folgen Sie der Anleitung in diesem Abschnitt, um Ihre Datenbank für optimale Leistung in SQLite zu konfigurieren.
Write-Ahead-Logging aktivieren
SQLite implementiert Änderungen, indem sie an ein Log angehängt werden, das gelegentlich in die Datenbank komprimiert wird. Dies wird als Write-Ahead-Logging (WAL) bezeichnet.
Aktivieren Sie WAL
es sei denn, Sie verwenden ATTACH DATABASE.
Synchronisierungsmodus lockern
Bei Verwendung von WAL wird standardmäßig bei jedem Commit ein fsync ausgegeben, um sicherzustellen, dass die Daten auf das Laufwerk geschrieben werden. Dadurch wird die Datenbeständigkeit verbessert, aber die Commits werden verlangsamt.
SQLite bietet eine Option zur Steuerung des synchronen Modus. Wenn Sie WAL aktivieren, setzen Sie den synchronen Modus auf NORMAL:
// When opening the database
val paramsBuilder: SQLiteDatabase.OpenParams.Builder = SQLiteDatabase.OpenParams.Builder()
paramsBuilder.journalMode = SQLiteDatabase.SYNC_MODE_NORMAL
// Or: after having opened the database
db.execSQL("PRAGMA synchronous = NORMAL");
Bei dieser Einstellung kann ein Commit zurückgegeben werden, bevor die Daten auf einer Festplatte gespeichert sind. Wenn ein Gerät heruntergefahren wird, z. B. bei einem Stromausfall oder einer Kernel-Panik, können die übertragenen Daten verloren gehen. Aufgrund der Protokollierung wird Ihre Datenbank jedoch nicht beschädigt.
Wenn nur Ihre App abstürzt, werden Ihre Daten trotzdem auf die Festplatte geschrieben. Bei den meisten Apps führt diese Einstellung zu Leistungsverbesserungen ohne nennenswerte Kosten.
Effiziente Tabellenschemas definieren
Um die Leistung zu optimieren und den Datenverbrauch zu minimieren, definieren Sie ein effizientes Tabellenschema. SQLite erstellt effiziente Abfragepläne und Daten, was zu einem schnelleren Abrufen von Daten führt. Dieser Abschnitt enthält Best Practices zum Erstellen von Tabellenschemas.
INTEGER PRIMARY KEY berücksichtigen
Definieren und füllen Sie in diesem Beispiel eine Tabelle so:
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 | id | Name | Stadt |
|---|---|---|---|
| 1 | 456 | John Lennon | Liverpool, England |
| 2 | 123 | Michael Jackson | Gary, IN |
| 3 | 789 | Dolly Parton | Sevier County, TN |
Die Spalte rowid ist
ein Index, der die Einfügereihenfolge beibehält. Abfragen, die
nach rowid filtern, werden als schnelle B-Baum-Suche implementiert, während Abfragen, die
nach id filtern, ein langsamer Tabellenscan sind.
Wenn Sie Suchen nach id durchführen möchten, können Sie die
rowid Spalte vermeiden, um weniger Daten im Speicher zu speichern und eine insgesamt
schnellere Datenbank zu erhalten:
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, TN |
Da Sie die rowid Spalte nicht speichern müssen, sind id Abfragen schnell. Beachten Sie, dass die Tabelle jetzt nach id und nicht nach der Einfügereihenfolge sortiert ist.
Abfragen mit Indexen beschleunigen
SQLite verwendet
Indexe
, um Abfragen zu beschleunigen. Wenn Sie eine Spalte filtern (WHERE), sortieren (ORDER BY) oder aggregieren (GROUP BY) und die Tabelle einen Index für die Spalte hat, wird die Abfrage beschleunigt.
Im vorherigen Beispiel erfordert das Filtern nach city das Scannen der gesamten Tabelle:
SELECT id, name
WHERE city = 'London, England';
Bei einer App mit vielen Abfragen nach Städten können Sie diese Abfragen mit einem Index beschleunigen:
CREATE INDEX city_index ON Customers(city);
Ein Index wird als zusätzliche Tabelle implementiert, die nach der Indexspalte sortiert und rowid zugeordnet ist:
| Stadt | rowid |
|---|---|
| Gary, IN | 2 |
| Liverpool, England | 1 |
| Sevier County, TN | 3 |
Beachten Sie, dass sich die Speicherkosten für die Spalte city jetzt verdoppelt haben, da sie sowohl in der ursprünglichen Tabelle als auch im Index vorhanden ist. Da Sie den Index verwenden, sind die Kosten für zusätzlichen Speicher den Vorteil schnellerer Abfragen wert.
Verwalten Sie jedoch keinen Index, den Sie nicht verwenden, um die Speicherkosten ohne Leistungssteigerung bei Abfragen zu vermeiden.
Mehrspaltige Indexe erstellen
Wenn Ihre Abfragen mehrere Spalten kombinieren, können Sie mehrspaltige Indexe erstellen, um die Abfrage vollständig zu beschleunigen. Sie können auch einen Index für eine äußere Spalte verwenden und die innere Suche als linearen Scan durchführen lassen.
Beispiel: Bei der folgenden Abfrage:
SELECT id, name
WHERE city = 'London, England'
ORDER BY city, name
Sie können die Abfrage mit einem mehrspaltigen Index in derselben Reihenfolge wie in der Abfrage angegeben beschleunigen:
CREATE INDEX city_name_index ON Customers(city, name);
Wenn Sie jedoch nur einen Index für city haben, wird die äußere Sortierung weiterhin beschleunigt, während für die innere Sortierung ein linearer Scan erforderlich ist.
Dies funktioniert auch bei Präfixabfragen. Ein Index
ON Customers (city, name) beschleunigt beispielsweise auch das Filtern, Sortieren und Gruppieren
nach city, da die Indextabelle für einen mehrspaltigen Index nach den
angegebenen Indexen in der angegebenen Reihenfolge sortiert ist.
WITHOUT ROWID berücksichtigen
Standardmäßig erstellt SQLite eine Spalte rowid für Ihre Tabelle, wobei rowid ein impliziter INTEGER PRIMARY KEY AUTOINCREMENT ist. Wenn Sie bereits eine Spalte mit INTEGER PRIMARY KEY haben, wird diese Spalte zu einem Alias von rowid.
Für Tabellen, die einen anderen Primärschlüssel als INTEGER oder eine Kombination von
Spalten haben, sollten Sie WITHOUT ROWID verwenden.
Kleine Daten als BLOB und große Daten als Datei speichern
Wenn Sie große Daten mit einer Zeile verknüpfen möchten, z. B. ein Miniaturbild 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 Pfad in der Spalte speichern.
Dateien werden in der Regel auf 4-KB-Schritte aufgerundet. Bei sehr kleinen Dateien, bei denen der Rundungsfehler erheblich ist, ist es effizienter, sie als BLOB in der Datenbank zu speichern. SQLite minimiert Dateisystemaufrufe und ist
schneller als das zugrunde liegende Dateisystem in einigen Fällen.
Abfrageleistung verbessern
Beachten Sie diese Best Practices, um die Abfrageleistung in SQLite zu verbessern, indem Sie die Antwortzeiten minimieren und die Verarbeitungseffizienz maximieren.
Nur die benötigten Zeilen lesen
Mit Filtern können Sie die Ergebnisse eingrenzen, indem Sie bestimmte Kriterien wie Datumsbereich, Standort oder Name angeben. Mit Limits können Sie die Anzahl der angezeigten Ergebnisse steuern:
db.rawQuery("""
SELECT name
FROM Customers
LIMIT 10;
""".trimIndent(),
null
).use { cursor ->
while (cursor.moveToNext()) {
// Process cursor data
}
}
Nur die benötigten Spalten lesen
Vermeiden Sie das Auswählen unnötiger Spalten, da dies Ihre Abfragen verlangsamen und Ressourcen verschwenden kann. Wählen Sie stattdessen nur die verwendeten Spalten aus.
Im folgenden Beispiel wählen Sie id, name und phone aus:
// 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)
// Further processing
}
}
Sie benötigen jedoch nur die Spalte name:
db.rawQuery("""
SELECT name
FROM Customers;
""".trimIndent(),
null
).use { cursor ->
while (cursor.moveToNext()) {
val name = cursor.getString(0)
// Further processing
}
}
Abfragen parametrisieren
Ihr Abfragestring kann einen Parameter enthalten, der erst zur Laufzeit bekannt ist, z. B.:
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
}
}
}
Im vorherigen Code erstellt jede Abfrage einen anderen String und profitiert daher nicht vom Anweisungscache. Bei jedem Aufruf muss SQLite die Abfrage kompilieren, bevor sie ausgeführt werden kann. Stattdessen können Sie das Argument id durch einen
Parameter ersetzen und
den Wert mit selectionArgs binden:
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
}
}
}
Jetzt kann die Abfrage einmal kompiliert und im Cache gespeichert werden. Die kompilierte Abfrage wird bei verschiedenen Aufrufen von getNameById(long) wiederverwendet.
In SQL und nicht im Code iterieren
Verwenden Sie eine einzelne Abfrage, die alle Zielergebnisse zurückgibt, anstelle einer programmatischen Schleife, die SQL-Abfragen durchläuft, um einzelne Ergebnisse zurückzugeben. Die programmatische Schleife ist etwa 1.000-mal langsamer als eine einzelne SQL-Abfrage.
DISTINCT für eindeutige Werte verwenden
Mit dem Schlüsselwort DISTINCT können Sie die Leistung Ihrer Abfragen verbessern, indem Sie die Menge der zu verarbeitenden Daten reduzieren. Wenn Sie beispielsweise nur die eindeutigen Werte aus einer Spalte zurückgeben möchten, verwenden Sie DISTINCT:
db.rawQuery("""
SELECT DISTINCT name
FROM Customers;
""".trimIndent(),
null
).use { cursor ->
while (cursor.moveToNext()) {
// Only iterate over distinct names in Kotlin
// Process distinct name
}
}
Aggregatfunktionen nach Möglichkeit verwenden
Verwenden Sie Aggregatfunktionen für aggregierte Ergebnisse ohne Zeilendaten. Der folgende Code prüft beispielsweise, ob mindestens eine übereinstimmende Zeile vorhanden ist:
// 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
// Handle found
} else {
// No customers from Paris
// Handle not found
}
Wenn Sie nur die erste Zeile abrufen möchten, können Sie EXISTS() verwenden, um 0 zurückzugeben, wenn keine übereinstimmende Zeile vorhanden ist, und 1, wenn eine oder mehrere Zeilen übereinstimmen:
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
// Handle found
} else {
// No customers from Paris
// Handle not found
}
}
Verwenden Sie SQLite-Aggregatfunktionen in Ihrem App Code:
COUNT: Zählt die Anzahl der Zeilen in einer Spalte.SUM: Addiert alle numerischen Werte in einer Spalte.MINoderMAX: Bestimmt den niedrigsten oder höchsten Wert. Funktioniert für numerische Spalten,DATE-Typen und Texttypen.AVG: Berechnet den durchschnittlichen numerischen Wert.GROUP_CONCAT: Verkettet Strings mit einem optionalen Trennzeichen.
COUNT() anstelle von Cursor.getCount() verwenden
Im
folgenden Beispiel liest die
Cursor.getCount() Funktion
alle Zeilen aus der Datenbank und gibt alle Zeilenwerte zurück:
// 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()
// Use count
}
Wenn Sie jedoch COUNT() verwenden, gibt die Datenbank nur die Anzahl zurück:
db.rawQuery("""
SELECT COUNT(*)
FROM Customers;
""".trimIndent(),
null
).use { cursor ->
cursor.moveToFirst()
val count = cursor.getInt(0)
// Use count
}
Abfragen anstelle von Code verschachteln
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 App-Code zu verwenden. Dadurch müssen keine Daten aus SQLite kopiert werden und die Datenbank-Engine kann Ihre Abfrage optimieren.
Im folgenden Beispiel können Sie eine Abfrage ausführen, um herauszufinden, in welcher Stadt die meisten Kunden sind, und dann das Ergebnis in einer anderen Abfrage verwenden, um alle Kunden aus dieser Stadt zu finden:
// 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()) {
// Process inner cursor data
}
}
}
}
Um das Ergebnis in der Hälfte der Zeit des vorherigen Beispiels zu erhalten, verwenden Sie eine einzelne SQL-Abfrage mit verschachtelten Anweisungen:
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()) {
// Process cursor data
}
}
Eindeutigkeit in SQL prüfen
Wenn eine Zeile nur eingefügt werden darf, wenn ein bestimmter Spaltenwert in der Tabelle eindeutig ist, ist es möglicherweise effizienter, diese Eindeutigkeit als Spalteneinschränkung zu erzwingen.
Im folgenden Beispiel wird eine Abfrage ausgeführt, um die einzufügende Zeile zu validieren, und eine andere, um sie tatsächlich einzufügen:
// 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
)
)
Anstatt die eindeutige Einschränkung in Kotlin zu prüfen, können Sie sie in SQL prüfen, wenn Sie die Tabelle definieren:
CREATE TABLE Customers(
id INTEGER PRIMARY KEY,
name TEXT,
username TEXT UNIQUE
);
SQLite macht dasselbe wie Folgendes:
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:
try {
db.execSql(
"INSERT INTO Customers VALUES (?, ?, ?)",
arrayOf(customer.id.toString(), customer.name, customer.username)
)
} catch(e: SQLiteConstraintException) {
throw 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-Code. Es ist eine Best Practice, SQLite anstelle von App-Code zu verwenden.
Mehrere Einfügungen in einem Batch in einer einzelnen Transaktion
Bei einer Transaktion werden mehrere Vorgänge übertragen, was nicht nur die Effizienz, sondern auch die Richtigkeit verbessert. Um die Datenkonsistenz zu verbessern und die Leistung zu steigern, können Sie Einfügungen in einem Batch ausführen:
db.beginTransaction()
try {
customers.forEach { customer ->
db.execSql(
"INSERT INTO Customers VALUES (?, ?, ?)",
arrayOf(customer.id.toString(), customer.name, "customerValue")
)
}
} finally {
db.endTransaction()
}
Tools zur Fehlerbehebung verwenden
SQLite bietet die folgenden Tools zur Fehlerbehebung, mit denen Sie die Leistung messen können.
Interaktive Eingabeaufforderung von SQLite verwenden
Führen Sie SQLite auf Ihrem Computer aus, um Abfragen auszuführen und zu lernen.
Verschiedene Android-Plattformversionen verwenden unterschiedliche Revisionen von SQLite. Wenn Sie dieselbe Engine verwenden möchten, die auf einem Android-Gerät verwendet wird, verwenden Sie adb shell und führen Sie sqlite3 auf Ihrem Zielgerät aus.
Sie können SQLite bitten, die Zeit für Abfragen zu messen:
sqlite> .timer on
sqlite> SELECT ...
Run Time: real ... user ... sys ...
EXPLAIN QUERY PLAN
Sie können SQLite mit
EXPLAIN QUERY PLAN erklären lassen, wie es eine Abfrage beantworten möchte:
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 Kunden aus Paris zu finden. Dies wird als lineare Komplexität bezeichnet. SQLite muss alle Zeilen lesen und nur die Zeilen behalten, die mit Kunden aus Paris übereinstimmen. Um dieses 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 immer zu erklären:
sqlite> .eqp on
Weitere Informationen finden Sie unter Abfrageplanung.
SQLite Analyzer
SQLite bietet die sqlite3_analyzer Befehlszeile, mit der Sie
zusätzliche Informationen ausgeben können, die zur Fehlerbehebung bei der Leistung verwendet werden können. Informationen zur Installation finden Sie auf der SQLite-Downloadseite.
Mit adb pull können Sie eine Datenbankdatei von einem Zielgerät zur Analyse auf Ihre Workstation herunterladen:
adb pull /data/data/<app_package_name>/databases/<db_name>.db
SQLite Browser
Sie können auch das GUI-Tool SQLite Browser auf der SQLite Downloadseite installieren.
Android-Protokollierung
Android misst die Zeit für SQLite-Abfragen und protokolliert sie für Sie:
# Enable query time logging
$ adb shell setprop log.tag.SQLiteTime VERBOSE
# Disable query time logging
$ adb shell setprop log.tag.SQLiteTime ERROR
Perfetto-Tracing
Wenn Sie Perfetto konfigurieren, können Sie Folgendes hinzufügen, um Tracks für einzelne Abfragen einzuschließen:
data_sources {
config {
name: "linux.ftrace"
ftrace_config {
atrace_categories: "database"
}
}
}
dumpsys meminfo
adb shell dumpsys meminfo <package-name> gibt Statistiken zur Arbeitsspeichernutzung der App aus, einschließlich einiger Details zum SQLite-Arbeitsspeicher. Das folgende Beispiel stammt aus der Ausgabe von adb shell dumpsys meminfo com.google.android.gms.persistent auf dem Gerät eines Entwicklers:
DATABASES
pgsz dbsz Lookaside(b) cache hits cache misses cache size Dbname
PER CONNECTION STATS
4 52 45 8 41 6 /data/user/10/com.google.android.gms/databases/gaia-discovery
4 8 0 0 0 (attached) temp
4 52 56 5 23 6 /data/user/10/com.google.android.gms/databases/gaia-discovery (1)
4 252 95 233 124 12 /data/user_de/10/com.google.android.gms/databases/phenotype.db
4 8 0 0 0 (attached) temp
4 252 17 0 17 1 /data/user_de/10/com.google.android.gms/databases/phenotype.db (1)
4 9280 105 103169 69805 25 /data/user/10/com.google.android.gms/databases/phenotype.db
4 20 0 0 0 (attached) temp
4 9280 108 13877 6394 25 /data/user/10/com.google.android.gms/databases/phenotype.db (2)
4 8 0 0 0 (attached) temp
4 9280 105 12548 5519 25 /data/user/10/com.google.android.gms/databases/phenotype.db (3)
4 8 0 0 0 (attached) temp
4 9280 107 18328 7886 25 /data/user/10/com.google.android.gms/databases/phenotype.db (1)
4 8 0 0 0 (attached) temp
4 36 51 156 29 5 /data/user/10/com.google.android.gms/databases/mobstore_gc_db_v0
4 36 97 47 27 10 /data/user/10/com.google.android.gms/databases/context_feature_default.db
4 36 56 3 16 4 /data/user/10/com.google.android.gms/databases/context_feature_default.db (2)
4 300 40 2111 24 5 /data/user/10/com.google.android.gms/databases/gservices.db
4 300 39 3 17 4 /data/user/10/com.google.android.gms/databases/gservices.db (1)
4 20 17 0 14 1 /data/user/10/com.google.android.gms/databases/gms.notifications.db
4 20 33 1 15 2 /data/user/10/com.google.android.gms/databases/gms.notifications.db (1)
4 120 40 143 163 4 /data/user/10/com.google.android.gms/databases/android_pay
4 120 123 86 32 19 /data/user/10/com.google.android.gms/databases/android_pay (1)
4 28 33 4 17 3 /data/user/10/com.google.android.gms/databases/googlesettings.db
POOL STATS
cache hits cache misses cache size Dbname
13 68 81 /data/user/10/com.google.android.gms/databases/gaia-discovery
233 145 378 /data/user_de/10/com.google.android.gms/databases/phenotype.db
147921 89616 237537 /data/user/10/com.google.android.gms/databases/phenotype.db
156 30 186 /data/user/10/com.google.android.gms/databases/mobstore_gc_db_v0
50 57 107 /data/user/10/com.google.android.gms/databases/context_feature_default.db
2114 43 2157 /data/user/10/com.google.android.gms/databases/gservices.db
1 31 32 /data/user/10/com.google.android.gms/databases/gms.notifications.db
229 197 426 /data/user/10/com.google.android.gms/databases/android_pay
4 18 22 /data/user/10/com.google.android.gms/databases/googlesettings.db
Unter DATABASES finden Sie Folgendes:
pgsz: Die Größe einer Datenbankseite in KB.dbsz: Die Größe der gesamten Datenbank in Seiten. Um die Größe in KB zu erhalten, multiplizieren Siepgszmitdbsz.Lookaside(b): Der für den SQLite-Lookaside-Puffer pro Verbindung zugewiesene Speicher in Byte. Diese sind in der Regel sehr klein.cache hits: SQLite verwaltet einen Cache von Datenbankseiten. Dies ist die Anzahl der Cachetreffer auf Seitenebene.cache misses: Anzahl der Cache-Fehler auf Seitenebene.cache size: Anzahl der Seiten im Cache. Um die Größe in KB zu erhalten, multiplizieren Sie diese Zahl mitpgsz.Dbname: Pfad zur Datenbankdatei. In unserem Beispiel haben einige Datenbanken(1)oder eine andere Zahl an ihren Namen angehängt, um anzugeben, dass es mehr als eine Verbindung zur selben zugrunde liegenden Datenbank gibt. Statistiken werden pro Verbindung erfasst.
Unter POOL STATS finden Sie Folgendes:
cache hits: SQLite speichert vorbereitete Anweisungen im Cache und versucht, sie bei der Ausführung von Abfragen wiederzuverwenden, um Aufwand und Speicher bei der Kompilierung von SQL-Anweisungen zu sparen. Dies ist die Anzahl der Cachetreffer auf Anweisungsebene.cache misses: Anzahl der Cache-Fehler auf Anweisungsebene.cache size: Ab Android 17 wird hier die Gesamtzahl der vorbereiteten Anweisungen im Cache aufgeführt. In früheren Versionen entspricht dieser Wert der Summe der Treffer und Fehler in den anderen beiden Spalten und stellt nicht die Cachegröße dar.
Zusätzliche Ressourcen
Inhalte ansehen
Empfehlungen für Sie
- Hinweis: Linktext wird angezeigt, wenn JavaScript deaktiviert ist
- Benchmarks in der Continuous Integration ausführen
- Eingefrorene Frames
- Baseline-Profile ohne Macrobenchmark erstellen und messen