SQLite performansı için en iyi uygulamalar

Android, verimli bir SQL veritabanı olan SQLite için yerleşik destek sunar. Uygulamanızın performansını optimize etmek için aşağıdaki en iyi uygulamaları uygulayarak verileriniz arttıkça hızlı ve tahmin edilebilir bir şekilde hızlı kalmasını sağlayın. Bu en iyi uygulamaları kullanarak, yeniden oluşturması ve sorunlarını gidermesi zor performans sorunlarınla karşılaşma olasılığını da azaltırsınız.

Daha hızlı performans elde etmek için aşağıdaki performans ilkelerini uygulayın:

  • Daha az satır ve sütun okuma: Sorgularınızı yalnızca gerekli verileri alacak şekilde optimize edin. Fazla veri alma performansı etkileyebileceğinden, veritabanından okunan veri miktarını en aza indirin.

  • İşi SQLite motoruna gönderin: SQL sorgularında hesaplama, filtreleme ve sıralama işlemleri gerçekleştirin. SQLite'in sorgu motorunu kullanmak performansı önemli ölçüde artırabilir.

  • Veritabanı şemasını değiştirme: Veritabanı şemanızı, SQLite'in etkili sorgu planları ve veri temsilleri oluşturmasına yardımcı olacak şekilde tasarlayın. Tabloları doğru şekilde dizine ekleyin ve performansı artırmak için tablo yapılarını optimize edin.

Ayrıca, optimizasyon gerektiren alanları belirlemek için SQLite veritabanınızın performansını ölçmek üzere mevcut sorun giderme araçlarını kullanabilirsiniz.

Jetpack Room kitaplığını kullanmanızı öneririz.

Veritabanını performans için yapılandırma

Veritabanını SQLite'te optimum performans için yapılandırmak üzere bu bölümdeki adımları uygulayın.

Önceden Yazma Günlük Kaydını Etkinleştirme

SQLite, mutasyonları bir günlüke ekleyerek uygular ve bu günlük zaman zaman veritabanında sıkıştırılır. Buna önceden yazma günlüğü (WAL) denir.

ATTACH DATABASE kullanmıyorsanız WAL'ı etkinleştirin.

Senkronizasyon modunu gevşetme

WAL kullanılırken varsayılan olarak her bir taahhüt, verilerin diske ulaşmasını sağlamak için bir fsync yayınlar. Bu, verilerin dayanıklılığını artırır ancak taahhütlerinizi yavaşlatır.

SQLite'te senkron modunu kontrol etme seçeneği vardır. WAL'yi etkinleştirirseniz senkronize modu NORMAL olarak ayarlayın:

Kotlin

db.execSQL("PRAGMA synchronous = NORMAL")

Java

db.execSQL("PRAGMA synchronous = NORMAL");

Bu ayarda, veriler diske kaydedilmeden önce bir taahhüt döndürülebilir. Güç kaybı veya çekirdek paniği gibi durumlarda cihaz kapanırsa tescilli veriler kaybolabilir. Ancak günlük kaydı sayesinde veritabanınız bozulmadı.

Yalnızca uygulamanız kilitlenirse verileriniz diske ulaşır. Çoğu uygulama için bu ayar, önemli bir maliyete yol açmadan performans iyileştirmeleri sağlar.

Verimli tablo şemaları tanımlama

Performansı optimize etmek ve veri tüketimini en aza indirmek için verimli bir tablo şeması tanımlayın. SQLite, verimli sorgu planları ve veriler oluşturur. Bu da daha hızlı veri almayla sonuçlanır. Bu bölümde, tablo şemaları oluşturmayla ilgili en iyi uygulamalar yer almaktadır.

INTEGER PRIMARY KEY

Bu örnekte, bir tabloyu aşağıdaki gibi tanımlayıp doldurun:

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');

Tablo çıkışı şu şekildedir:

rowid id ad şehir
1 456 John Lennon Liverpool, İngiltere
2 123 Michael Jackson Gary, Indiana
3 789 Dolly Parton Sevier County, TN

rowid sütunu, kampanya siparişini koruyan bir dizindir. rowid değerine göre filtrelenen sorgular hızlı bir B ağacı araması olarak uygulanır ancak id değerine göre filtrelenen sorgular yavaş bir tablo taraması olarak uygulanır.

id değerine göre arama yapmayı planlıyorsanız depolama alanında daha az veri ve genel olarak daha hızlı bir veritabanı için rowid sütununu depolamamaktan kaçınabilirsiniz:

CREATE TABLE Customers(
  id INTEGER PRIMARY KEY,
  name TEXT,
  city TEXT
);

Tablonuz artık aşağıdaki gibi görünür:

id ad şehir
123 Michael Jackson Gary, Indiana
456 John Lennon Liverpool, İngiltere
789 Dolly Parton Sevier County, TN

rowid sütununu depolamanıza gerek olmadığından id sorguları hızlıdır. Tablonun artık kampanya siparişi yerine id'ye göre sıralandığını unutmayın.

Dizinlerle sorguları hızlandırma

SQLite, sorguları hızlandırmak için dizinler kullanır. Bir sütun filtrelenirken (WHERE), sıralanırken (ORDER BY) veya toplanırken (GROUP BY), tabloda sütun için bir dizin varsa sorgu hızlandırılır.

Önceki örnekte, city değerine göre filtreleme yapmak için tablonun tamamının taranması gerekir:

SELECT id, name
WHERE city = 'London, England';

Çok sayıda şehir sorgusu olan bir uygulamada, bu sorguları bir dizinle hızlandırabilirsiniz:

CREATE INDEX city_index ON Customers(city);

Dizin, dizin sütununa göre sıralanmış ve rowid ile eşlenmiş ek bir tablo olarak uygulanır:

şehir rowid
Gary, Indiana 2
Liverpool, İngiltere 1
Sevier County, TN 3

city sütununun depolama maliyetinin artık iki katı olduğunu unutmayın çünkü artık hem orijinal tabloda hem de dizinde mevcuttur. Dizin kullandığınızdan, ek depolama alanı maliyeti daha hızlı sorguların avantajına değer. Ancak, sorgu performansında herhangi bir artış elde etmeden depolama alanı maliyeti ödememek için kullanmadığınız bir dizini muhafaza etmeyin.

Çok sütunlu dizinler oluşturma

Sorgularınız birden fazla sütunu birleştiriyorsa sorguyu tamamen hızlandırmak için çok sütunlu dizinler oluşturabilirsiniz. Dıştaki bir sütunda dizin de kullanabilir ve iç aramanın doğrusal tarama olarak yapılmasına izin verebilirsiniz.

Örneğin, aşağıdaki sorgu verildiğinde:

SELECT id, name
WHERE city = 'London, England'
ORDER BY city, name

Sorguda belirtilen sırayla çok sütunlu bir dizin kullanarak sorguyu hızlandırabilirsiniz:

CREATE INDEX city_name_index ON Customers(city, name);

Ancak yalnızca city üzerinde bir dizininiz varsa dış sipariş yine de hızlandırılır. İç sipariş ise doğrusal tarama gerektirir.

Bu yöntem, ön ek sorguları için de geçerlidir. Örneğin, çok sütunlu bir dizinin dizin tablosu, belirli dizine göre belirli bir sırada sıralandığından ON Customers (city, name) dizini, city'ye göre filtreleme, sıralama ve gruplandırma işlemlerini de hızlandırır.

WITHOUT ROWID

SQLite, varsayılan olarak tablonuz için bir rowid sütunu oluşturur. Burada rowid, gizli bir INTEGER PRIMARY KEY AUTOINCREMENT olur. INTEGER PRIMARY KEY adlı bir sütununuz varsa bu sütun rowid için bir takma ad olur.

INTEGER dışında bir birincil anahtarı veya sütunlardan oluşan bir bileşimi olan tablolar için WITHOUT ROWID seçeneğini kullanın.

Küçük verileri BLOB olarak, büyük verileri ise dosya olarak depolama

Bir satırla ilişkilendirmek istediğiniz büyük verileri (ör. bir resmin küçük resmi veya bir kişinin fotoğrafı) BLOB sütununda ya da bir dosyada saklayabilir ve ardından dosya yolunu sütunda saklayabilirsiniz.

Dosyalar genellikle 4 KB'lık artışlarla yuvarlanır. Yuvarlama hatasının önemli olduğu çok küçük dosyaları veritabanında BLOB olarak depolamak daha verimlidir. SQLite, dosya sistemi çağrılarını en aza indirir ve bazı durumlarda temel dosya sisteminden daha hızlıdır.

Sorgu performansını iyileştirme

Yanıt sürelerini en aza indirip işleme verimliliğini en üst düzeye çıkararak SQLite'teki sorgu performansını iyileştirmek için aşağıdaki en iyi uygulamaları izleyin.

Yalnızca ihtiyacınız olan satırları okuma

Filtreler, tarih aralığı, konum veya ad gibi belirli ölçütleri belirterek sonuçlarınızı daraltmanıza olanak tanır. Sınırlar, gördüğünüz sonuç sayısını kontrol etmenizi sağlar:

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()) {
    ...
  }
}

Yalnızca ihtiyacınız olan sütunları okuma

Sorgularınızı yavaşlatabilecek ve kaynakları boşa harcayabilecek gereksiz sütunları seçmekten kaçının. Bunun yerine, yalnızca kullanılan sütunları seçin.

Aşağıdaki örnekte id, name ve phone öğelerini seçersiniz:

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);
    ...
  }
}

Ancak yalnızca name sütununa ihtiyacınız vardır:

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);
    ...
  }
}

Sorguları dize birleştirme yerine SQL kartlarıyla parametrelendirin

Sorgu diziniz yalnızca çalışma zamanında bilinen bir parametre içerebilir. Örneğin:

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;
    }
  }
}

Önceki kodda her sorgu farklı bir dize oluşturur ve bu nedenle ifade önbelleğinden yararlanmaz. Her çağrının yürütülebilmesi için SQLite'nin derlenmesi gerekir. Bunun yerine, id bağımsız değişkenini bir parametre ile değiştirebilir ve değeri selectionArgs ile bağlayabilirsiniz:

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;
    }
  }
}

Artık sorgu bir kez derlenebilir ve önbelleğe alınabilir. Derlenen sorgu, getNameById(long)'ün farklı çağrıları arasında yeniden kullanılır.

Kodda değil, SQL'de iterasyon yapın

Tek tek sonuçlar döndürmek için SQL sorgularını yineleyen programatik bir döngü yerine, hedeflenen tüm sonuçları döndüren tek bir sorgu kullanın. Programatik döngü, tek bir SQL sorgusundan yaklaşık 1.000 kat daha yavaştır.

Benzersiz değerler için DISTINCT kullanın

DISTINCT anahtar kelimesini kullanmak, işlenecek veri miktarını azaltarak sorgularınızın performansını artırabilir. Örneğin, bir sütundaki yalnızca benzersiz değerleri döndürmek istiyorsanız DISTINCT kullanın:

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
    ...
  }
}

Mümkün olduğunda toplama işlevlerini kullanın

Satır verileri içermeyen toplu sonuçlar için toplama işlevlerini kullanın. Örneğin, aşağıdaki kod en az bir eşleşen satır olup olmadığını kontrol eder:

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
    ...
  }
}

Yalnızca ilk satırı almak için eşleşen bir satır yoksa 0, bir veya daha fazla satır eşleşirse 1 döndürmek üzere EXISTS() kullanabilirsiniz:

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
    ...
  }
}

Uygulama kodunuzda SQLite toplu işlevlerini kullanın:

  • COUNT: Bir sütundaki satır sayısını hesaplar.
  • SUM: Bir sütundaki tüm sayısal değerleri toplar.
  • MIN veya MAX: En düşük veya en yüksek değeri belirler. Sayısal sütunlar, DATE türleri ve metin türleri için çalışır.
  • AVG: Ortalama sayısal değeri bulur.
  • GROUP_CONCAT: Dizeleri isteğe bağlı bir ayırıcıyla birleştirir.

Cursor.getCount() yerine COUNT() kullanın

Aşağıdaki örnekte, Cursor.getCount() işlevi veritabanındaki tüm satırları okur ve tüm satır değerlerini döndürür:

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();
  ...
}

Ancak COUNT() kullanıldığında veritabanı yalnızca sayıyı döndürür:

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);
  ...
}

Kod yerine Nest sorguları

SQL, birleştirilebilir ve alt sorgular, birleştirme işlemleri ve yabancı anahtar kısıtlamalarını destekler. Uygulama koduna girmeden bir sorgunun sonucunu başka bir sorguda kullanabilirsiniz. Bu sayede, SQLite'ten veri kopyalama ihtiyacı azalır ve veritabanı motoru sorgunuzu optimize edebilir.

Aşağıdaki örnekte, en çok müşterinin hangi şehirde olduğunu bulmak için bir sorgu çalıştırabilir ve ardından bu şehirdeki tüm müşterileri bulmak için sonucu başka bir sorguda kullanabilirsiniz:

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()) {
          ...
        }
    }
  }
}

Sonucu önceki örneğin yarısı kadar sürede almak için iç içe yerleştirilmiş ifadeler içeren tek bir SQL sorgusu kullanın:

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()) {
    ...
  }
}

SQL'de benzersizliği kontrol etme

Belirli bir sütun değeri tabloda benzersiz olmadığı sürece satır eklenmemesi gerekiyorsa bu benzersizliği sütun kısıtlaması olarak zorunlu kılmak daha verimli olabilir.

Aşağıdaki örnekte, eklenecek satırı doğrulamak için bir sorgu, gerçekten eklemek için de başka bir sorgu çalıştırılır:

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,
    });

Benzersiz kısıtlamayı Kotlin veya Java'da kontrol etmek yerine, tabloyu tanımlarken SQL'de kontrol edebilirsiniz:

CREATE TABLE Customers(
  id INTEGER PRIMARY KEY,
  name TEXT,
  username TEXT UNIQUE
);

SQLite, aşağıdakiyle aynı işlemi yapar:

CREATE TABLE Customers(...);
CREATE UNIQUE INDEX CustomersUsername ON Customers(username);

Artık bir satır ekleyebilir ve SQLite'in kısıtlamayı kontrol etmesine izin verebilirsiniz:

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, birden çok sütun içeren benzersiz dizinleri destekler:

CREATE TABLE table(...);
CREATE UNIQUE INDEX unique_table ON table(column1, column2, ...);

SQLite, kısıtlamaları Kotlin veya Java koduna kıyasla daha hızlı ve daha az ek maliyetle doğrular. Uygulama kodu yerine SQLite kullanmak en iyi uygulamadır.

Tek bir işlemde birden fazla kampanya ekleme işlemini toplu olarak gerçekleştirme

İşlem, birden fazla işlemi taahhüt eder. Bu, yalnızca verimliliği değil doğruluğu da artırır. Veri tutarlılığını iyileştirmek ve performansı artırmak için eklemeleri toplu olarak yapabilirsiniz:

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()
}

Sorun giderme araçlarını kullanma

SQLite, performansı ölçmenize yardımcı olmak için aşağıdaki sorun giderme araçlarını sağlar.

SQLite'in etkileşimli istemlerini kullanma

Sorgu çalıştırmak ve öğrenmek için makinenizde SQLite'i çalıştırın. Farklı Android platform sürümleri, SQLite'in farklı düzeltmelerini kullanır. Android destekli bir cihazdakiyle aynı motoru kullanmak için adb shell kullanın ve hedef cihazınızda sqlite3'ı çalıştırın.

SQLite'ten sorguları zamanlamasını isteyebilirsiniz:

sqlite> .timer on
sqlite> SELECT ...
Run Time: real ... user ... sys ...

EXPLAIN QUERY PLAN

EXPLAIN QUERY PLAN kullanarak SQLite'ten bir sorguyu nasıl yanıtlamayı planladığını açıklamasını isteyebilirsiniz:

sqlite> EXPLAIN QUERY PLAN
SELECT id, name
FROM Customers
WHERE city = 'Paris';
QUERY PLAN
`--SCAN Customers

Önceki örnekte, Paris'teki tüm müşterileri bulmak için dizin olmadan tam tablo taraması gerekir. Buna doğrusal karmaşıklık denir. SQLite'in tüm satırları okuması ve yalnızca Paris'teki müşterilerle eşleşen satırları tutması gerekir. Bu sorunu düzeltmek için dizin ekleyebilirsiniz:

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=?

Etkileşimli kabuğu kullanıyorsanız SQLite'ten sorgu planlarını her zaman açıklamasını isteyebilirsiniz:

sqlite> .eqp on

Daha fazla bilgi için Sorgu Planlama bölümüne bakın.

SQLite Analiz Aracı

SQLite, performans sorunlarını gidermek için kullanılabilecek ek bilgileri dökmek amacıyla sqlite3_analyzer komut satırı arayüzünü (KSA) sunar. Yüklemek için SQLite İndirme Sayfası'nı ziyaret edin.

Bir veritabanı dosyasını hedef cihazdan analiz için iş istasyonunuza indirmek üzere adb pull'ü kullanabilirsiniz:

adb pull /data/data/<app_package_name>/databases/<db_name>.db

SQLite Tarayıcı

SQLite İndirilenler sayfasından SQLite Tarayıcı GUI aracını da yükleyebilirsiniz.

Android günlük kaydı

Android, SQLite sorgularını zamanlayıp sizin için günlüğe kaydeder:

# Enable query time logging
$ adb shell setprop log.tag.SQLiteTime VERBOSE
# Disable query time logging
$ adb shell setprop log.tag.SQLiteTime ERROR

Perfetto izleme

Perfetto'yu yapılandırırken, bağımsız sorgular için kanallar eklemek üzere aşağıdakileri ekleyebilirsiniz:

data_sources {
  config {
    name: "linux.ftrace"
    ftrace_config {
      atrace_categories: "database"
    }
  }
}