SQLite performansı için en iyi uygulamalar

Android, Android'de kullanıma sunulan SQLite için yerleşik destek daha verimli bir SQL veritabanıdır. Uygulamanızın performansını optimize etmek için Böylece, verileriniz büyüdükçe verilerinizin hızlı ve öngörülebilir hızda kalmasını sağlar. Bu en iyi uygulamalardan yararlanarak olası satışların sürekli olarak yeniden üretilmesi zor performans sorunlarıyla karşılaşan sorun giderme adımlarına göz atın.

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 işleyeceğiz. Veri tabanından okunan veri miktarını en aza indirin çünkü performansı etkileyebilir.

  • Çalışmayı SQLite Engine'e aktarın: Hesaplama, filtreleme ve sıralama işlemleri gerçekleştirin işlemleri gerçekleştirmelerini sağlar. SQLite'ın sorgu motorunu kullanarak performansı artırır.

  • Veritabanı şemasını değiştirme: Veritabanı şemanızı SQLite'a yardımcı olacak şekilde tasarlayın ve verimli sorgu planları ve veri gösterimleri oluşturma. Tabloları düzgün şekilde dizine ekleme ve performansı artırmak için tablo yapılarını optimize edin.

Ayrıca, sorun giderme araçlarını kullanarak gereken alanları belirlemenize yardımcı olması için SQLite veritabanınızın optimize edebilirsiniz.

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

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

Veritabanınızı optimum şekilde yapılandırmak için bu bölümdeki adımları uygulayın. performansı hakkında daha fazla bilgi edinin.

Yazma Öncesi Günlük Kaydını Etkinleştir

SQLite, mutasyonları zaman zaman bir günlüğe ekleyerek uygular. veritabanına sıkıştırılır. Buna Write-Ahead Logging (Yazma Öncesi Günlük Kaydı) adı verilir. (WAL) ile değiştirin.

Etkinleştir DUVAR (ATTACH DATABASE kullanmıyorsanız)

Senkronizasyon modunu gevşetme

WAL kullanırken, her kaydetme işlemi için varsayılan olarak bir fsync verilir. Bu sayede verinin diske ulaşması anlamına gelir. Bu işlem, verilerin dayanıklılığını artırır ancak kaydeder.

SQLite'ta, eşzamanlıları kontrol etme seçeneği bulunur mod'u seçin. Şu durumda: WAL'yi etkinleştir, eşzamanlı modu NORMAL olarak ayarla:

Kotlin

db.execSQL("PRAGMA synchronous = NORMAL")

Java

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

Bu ayarda, bir kaydetme işlemi, veriler diskte depolanmadan önce dönebilir. güç kesintisi veya çekirdek paniği gibi durumlarda cihazın kapanması kaydedilen veriler kaybolabilir. Ancak, günlük kaydı nedeniyle veritabanınız bozuk.

Yalnızca uygulamanız kilitlenirse verileriniz diske erişmeye devam eder. Çoğu uygulamada bu ayar, ayarı, herhangi bir maliyet olmadan 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ına bakalım. SQLite, verimli sorgu planları ve veriler oluşturur. Bu sayede, verileri daha hızlı almanıza yardımcı olur. Bu bölümde, tablo oluşturma ile ilgili en iyi uygulamalar oluşturabilirsiniz.

INTEGER PRIMARY KEY ürününü düşünün

Bu örnek için bir tabloyu aşağıdaki gibi tanımlayın ve 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 çıktısı aşağıdaki gibidir:

satır kimliği id ad şehir
1 456 Can Lennon Liverpool, İngiltere
2 123 Michael Jackson Gary, Indiana
3 789 Dolly Parton Sevier County, TN

rowid sütunu ekleme talimatını koruyan bir dizin. Sorguların rowid filtresi, hızlı bir B ağacı araması olarak uygulanır, ancak id filtresi yavaş bir tablo taramadır.

id tarihine kadar arama yapmayı planlıyorsanız, Depolama alanında daha az veri, toplamda ise daha az veri için rowid sütunu daha hızlı veritabanı:

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 Can Lennon Liverpool, İngiltere
789 Dolly Parton Sevier County, TN

rowid sütununu depolamanız gerekmediğinden, id sorguları hızlıdır. Not Böylece, tablonun artık kampanya siparişi yerine id temel alınarak sıralandığı gösteriliyor.

Dizinleri kullanarak sorguları hızlandırma

SQLite, dizinler kullanabilirsiniz. Filtreleme (WHERE), sıralama (ORDER BY) veya bir sütun toplanıyorsa (GROUP BY), tablonun sütun için bir dizini varsa sorgu hızlandırıldı.

Yukarıdaki örnekte, city ölçütüne 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, tek bir sorguyla bu sorguları hızlandırabilir dizin:

CREATE INDEX city_index ON Customers(city);

Bir dizin, ek bir tablo olarak uygulanır. Bu dizin, dizin sütununa göre sıralanır ve rowid ile eşlendi:

şehir satır kimliği
Gary, Indiana 2
Liverpool, İngiltere 1
Sevier County, TN 3

city sütununun depolama maliyetinin şu anda iki katına çıkacağını unutmayın, çünkü artık hem orijinal tabloda hem de dizinde bulunmaktadır. ek depolama alanı maliyeti, daha hızlı sorguların yararınadır. Ancak, ödeme yaparken kullanmadığınız bir dizini saklamayın. depolama maliyeti oluşturmaya devam eder.

Çok sütunlu dizinler oluşturma

Sorgularınız birden çok sütunu birleştiriyorsa, çok sütunlu dizinleri kullanabilirsiniz. Ayrıca, bir dış sütunda da bir dizin kullanabilirsiniz ve iç aramanın doğrusal tarama olarak yapılmasını sağlayın.

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

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

Çok sütunlu bir dizinle sorguyu aşağıdaki sırayla hızlandırabilirsiniz: sorguda belirtildiği gibi:

CREATE INDEX city_name_index ON Customers(city, name);

Ancak yalnızca city adresinde bir dizininiz varsa dış sıralama yine de olur dahili sıralama doğrusal tarama gerektirir.

Bu, önekli sorgularda da çalışır. Örneğin, ON Customers (city, name); filtreleme, sıralama ve gruplandırmayı da hızlandırır city değerine göre; çünkü çok sütunlu bir dizine ait dizin tablosu belirli bir sırada gösterir.

WITHOUT ROWID ürününü düşünün

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

INTEGER dışında bir birincil anahtarı veya sütun varsa WITHOUT ROWID seçeneğini değerlendirin.

Küçük verileri BLOB, büyük verileri ise dosya olarak depolayın

Büyük verileri bir satırla (ör. bir resmin küçük resmi) ilişkilendirmek istiyorsanız ya da kişinin fotoğrafını çekerek, verileri bir BLOB sütununda ya da bir dosya oluşturabilir ve ardından dosya yolunu sütunda depolayabilirsiniz.

Dosyalar genellikle 4 KB'lık artımlara yuvarlanır. Çok küçük dosyalar için çok büyükse, yuvarlama hatasını büyük bir bölmede BLOB olarak veritabanını kullanabilirsiniz. SQLite, dosya sistemi çağrılarını en aza indirir ve temel dosya sistemi bazı durumlarda kullanabilirsiniz.

Sorgu performansını iyileştirin

Şu en iyi uygulamaları izleyerek SQLite'ta sorgu performansını artırmak için ve işlem verimliliğini en üst düzeye çıkarıyor.

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

Filtreler, belirli kriterler belirleyerek sonuçlarınızı daraltmanıza olanak tanır. (ör. tarih aralığı, konum veya ad) Sınırlar, sayıyı kontrol etmenize olanak tanır gösterilen sonuç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ı okuyun

Gereksiz sütunlar seçmekten kaçının, aksi takdirde sorgularınızı yavaşlatabilir ve kaynaklarınızı israfa uğratabilir. Bunun yerine yalnızca sütunları seçin için de geçerli.

Aşağıdaki örnekte id, name ve phone'yi seçiyorsunuz:

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

Benzersiz değerler için DISTINCT kullanın

DISTINCT anahtar kelimesini kullanmak, sorgularınızın performansını şu şekilde artırabilir: Bu da işlenmesi gereken veri miktarını azaltıyor. Örneğin, bir sütundan yalnızca benzersiz değerleri döndürmek için DISTINCT işlevini 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 birleştirilmiş 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ı getirmek için EXISTS() kullanarak eşleşme varsa 0 değerini döndürebilirsiniz. satır mevcut değil ve bir veya daha çok satır eşleşiyorsa 1:

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 toplama özelliğini kullanma işlevleriyle ilgili daha fazla bilgi edinin. kod:

  • COUNT: Bir sütunda kaç satır olduğunu sayar.
  • SUM: Bir sütundaki tüm sayısal değerleri ekler.
  • MIN veya MAX: En düşük veya en yüksek değeri belirler. Sayısal öğeler için çalışır sütunlar, DATE türleri ve metin türleri.
  • 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ı:

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 composable'dır ve alt sorguları, birleştirmeleri ve yabancı anahtar kısıtlamalarını destekler. Bir sorgunun sonucunu, uygulamaya gitmeden başka bir sorguda kullanabilirsiniz. girin. Bu sayede SQLite'dan veri kopyalama ihtiyacı azalır ve veritabanı Google Analytics 4.0'dan ibarettir.

Aşağıdaki örnekte, şehirdeki en büyük şehirleri bulmak için sonra bu sonucu başka bir sorguda kullanarak şu şehir:

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

Önceki örneğin yarısında sonucu almak için tek bir SQL kullanın iç içe yerleştirilmiş ifadeler içeren bir sorgu:

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'deki benzersizliği kontrol edin

Belirli bir sütun değerinin bir sütun haline getirirse, bu benzersizliği bir sütun olarak uygulamak daha verimli olabilir. kısıtlayın.

Aşağıdaki örnekte, oluşturulacak satırı doğrulamak için bir sorgu çalıştırılmıştır eklendi:

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

Kotlin veya Java'daki benzersiz kısıtlamayı kontrol etmek yerine, Tabloyu tanımladığınızda SQL:

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

SQLite aşağıdakilerle aynı işlemleri yapar:

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

Şimdi bir satır ekleyip SQLite'ın kısıtlamayı kontrol etmesini sağlayabilirsiniz:

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ütunlu benzersiz dizinleri destekler:

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

SQLite, kısıtlamaları Kotlin veya Java'ya göre daha hızlı ve daha az ek yük ile doğrular girin. Uygulama kodu yerine SQLite kullanmak en iyi uygulamalardan biridir.

Tek bir işlemde birden çok ekleme işlemi yapma

Bir işlem birden fazla işlem gerçekleştirir, bu da hem de doğruluğu kapsar. Veri tutarlılığını artırmak ve performansı artırmak istiyorsanız toplu eklemeler 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çmeye yardımcı olmak için aşağıdaki sorun giderme araçlarını sunar.

SQLite'ın etkileşimli istemini kullanın

Sorgu çalıştırmak ve bilgi edinmek için makinenizde SQLite çalıştırın. Farklı Android platform sürümlerinde SQLite'ın farklı revizyonları kullanılır. Kullanılacak Android destekli bir cihazdakilerle aynı motoru kullanıyorsanız adb shell ve hedef cihazınızda sqlite3 uygulamasını çalıştırın.

SQLite'tan sorguları zamanlamasını isteyebilirsiniz:

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

EXPLAIN QUERY PLAN

SQLite'tan bir sorguyu yanıtlamanın amacını açıklamak için EXPLAIN QUERY PLAN:

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

Önceki örnekte, tüm sonuçları bulmak için dizin olmadan tam bir tablo taraması yapılmalıdır. bir teklif eklediğinizden emin olun. Buna doğrusal karmaşıklık denir. SQLite'ın okuması gerekiyor tüm satırları kaldırın ve yalnızca Paris'teki müşterilerle eşleşen satırları tutun. 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 kabuk kullanıyorsanız SQLite'tan her zaman sorgu planları:

sqlite> .eqp on

Daha fazla bilgi için bkz. Sorgu Planlama.

SQLite Analiz Aracı

SQLite, sqlite3_analyzer komut satırı arayüzü (KSA) kullanarak, performans sorunlarını gidermeye yardımcı olur. Yüklemek için SQLite İndirme Sayfası.

Bir hedef cihazdan cihazınıza veritabanı indirmek için adb pull kullanabilirsiniz analiz için iş istasyonu:

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

SQLite Tarayıcı

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

Android günlük kaydı

Android çarpı SQLite sorguları ve bunları 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 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"
    }
  }
}
ziyaret edin.