Best Practices für die SQLite-Leistung

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.
  • MIN oder MAX: 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 Sie pgsz mit dbsz.
  • 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 mit pgsz.
  • 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