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ı izleyin. Böylece, verileriniz büyüdükçe uygulamanızın hızlı ve öngörülebilir şekilde hızlı kalmasını sağlayabilirsiniz. Bu en iyi uygulamaları kullanarak, yeniden üretilmesi ve sorun giderilmesi zor olan performans sorunlarıyla 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. Aşırı veri alımı performansı etkileyebileceğinden, veritabanından okunan veri miktarını en aza indirin.
İşi SQLite motoruna aktarma: SQL sorgularında hesaplama, filtreleme ve sıralama işlemleri gerçekleştirin. SQLite'ın sorgu motorunu kullanmak performansı önemli ölçüde artırabilir.
Veritabanı şemasını değiştirme: SQLite'ın etkili sorgu planları ve veri temsilleri oluşturmasına yardımcı olacak şekilde veritabanı şemanızı tasarlayın. Tabloları düzgün ş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
SQLite'te veritabanınızı optimum performans için yapılandırmak üzere bu bölümdeki adımları uygulayın.
Yazma öncesi günlük kaydını etkinleştirme
SQLite, mutasyonları bir günlük dosyasına ekleyerek uygular. Bu günlük dosyası, zaman zaman veritabanında sıkıştırılır. Buna Yazma Öncesi Günlük Kaydı (WAL) denir.
ATTACH
DATABASE
kullanmıyorsanız WAL'ı etkinleştirin.
Senkronizasyon modunu rahatlatma
WAL kullanılırken varsayılan olarak her commit, verilerin diske ulaşmasını sağlamak için fsync
yayınlar. Bu, verilerin dayanıklılığını artırır ancak işlemelerinizi yavaşlatır.
SQLite'te senkron modunu kontrol etme seçeneği vardır. WAL'ı etkinleştirirseniz senkron modu NORMAL
olarak ayarlayın:
Kotlin
db.execSQL("PRAGMA synchronous = NORMAL")
Java
db.execSQL("PRAGMA synchronous = NORMAL");
Bu ayarda, veriler diske depolanmadan önce bir commit döndürülebilir. Güç kaybı veya çekirdek panik gibi bir cihaz kapatma işlemi gerçekleşirse kaydedilen veriler kaybolabilir. Ancak günlük kaydı nedeniyle veritabanınız bozulmaz.
Yalnızca uygulamanız kilitlenirse verileriniz diske ulaşmaya devam eder. Çoğu uygulama için bu ayar, önemli 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ı tanımlayın. SQLite, verimli sorgu planları ve veriler oluşturarak daha hızlı veri alımına olanak tanır. Bu bölümde, tablo şemaları oluşturmayla ilgili en iyi uygulamalar yer almaktadır.
INTEGER PRIMARY KEY
kategorisini 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:
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, ekleme sırasını koruyan bir dizindir. rowid
ile filtreleyen sorgular hızlı bir B-ağacı araması olarak uygulanır ancak id
ile filtreleyen sorgular yavaş bir tablo taramasıdır.
id
ile arama yapmayı planlıyorsanız depolama alanında daha az veri ve genel olarak daha hızlı bir veritabanı için rowid
sütununu depolamaktan kaçınabilirsiniz:
CREATE TABLE Customers(
id INTEGER PRIMARY KEY,
name TEXT,
city TEXT
);
Tablonuz artık aşağıdaki gibi görünüyor:
id | ad | şehir |
---|---|---|
123 | Michael Jackson | Gary, Indiana |
456 | John Lennon | Liverpool, İngiltere |
789 | Dolly Parton | Sevier County, TN |
rowid
sütununu saklamanız gerekmediğinden id
sorguları hızlıdır. Tablonun artık kampanya siparişine göre değil, id
'ya göre sıralandığını unutmayın.
Dizinlerle sorguları hızlandırma
SQLite, sorguları hızlandırmak için
dizinleri 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
ile 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. Bunun nedeni, sütunun hem orijinal tabloda hem de dizinde bulunmasıdır. Dizini kullandığınız için eklenen depolama alanının maliyeti, daha hızlı sorguların avantajına değer.
Ancak sorgu performansı artışı sağlamadan depolama maliyeti ödememek için kullanmadığınız bir dizini tutmayın.
Çok sütunlu dizinler oluşturma
Sorgularınız birden fazla sütunu birleştiriyorsa sorguyu tam olarak hızlandırmak için çok sütunlu dizinler oluşturabilirsiniz. Ayrıca, dış sütunda bir dizin kullanabilir ve iç aramanın doğrusal tarama olarak yapılmasını sağlayabilirsiniz.
Örneğin, şu sorgu verildiğinde:
SELECT id, name
WHERE city = 'London, England'
ORDER BY city, name
Sorguda belirtilen sırayla aynı olan çok sütunlu bir dizinle sorguyu hızlandırabilirsiniz:
CREATE INDEX city_name_index ON Customers(city, name);
Ancak yalnızca city
üzerinde bir dizininiz varsa dış sıralama yine hızlandırılırken iç sıralama için doğrusal tarama gerekir.
Bu özellik, önek sorgularıyla da çalışır. Örneğin, bir dizin
ON Customers (city, name)
, çok sütunlu bir dizinin dizin tablosu belirli bir sırayla belirli dizinlere göre sıralandığından city
ile filtreleme, sıralama ve gruplandırma işlemlerini de hızlandırır.
WITHOUT ROWID
kategorisini düşünün
SQLite, varsayılan olarak tablonuz için rowid
sütunu oluşturur. Burada rowid
, örtülü bir INTEGER PRIMARY KEY AUTOINCREMENT
'dir. Zaten INTEGER PRIMARY KEY
olan bir sütununuz varsa bu sütun, rowid
'nin diğer adı olur.
INTEGER
dışında bir birincil anahtarı olan veya sütunların bir bileşimi olan tablolar için WITHOUT
ROWID
'ı kullanabilirsiniz.
Küçük verileri BLOB
, büyük verileri ise dosya olarak depolama
Bir satırla büyük verileri (ör. resim küçük resmi veya kişi fotoğrafı) ilişkilendirmek istiyorsanız verileri BLOB
sütununda veya bir dosyada depolayabilir, 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ı artırma
Yanıt sürelerini en aza indirerek ve işlem verimliliğini en üst düzeye çıkararak SQLite'te sorgu performansını artırmak için aşağıdaki en iyi uygulamalardan yararlanın.
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çların sayısını kontrol etmenize olanak tanır:
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
Gereksiz sütunları seçmekten kaçının. Bu sütunlar sorgularınızı yavaşlatabilir ve kaynaklarınızı boşa harcayabilir. Bunun yerine, yalnızca kullanılan sütunları seçin.
Aşağıdaki örnekte id
, name
ve phone
seçilir:
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ı parametrelendirme
Sorgu dizeniz, 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şturduğundan ifade önbelleğinden yararlanmaz. Her çağrının yürütülebilmesi için önce SQLite tarafından derlenmesi gerekir. Bunun yerine, id
bağımsız değişkenini bir parametreyle 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 derlenip önbelleğe alınabilir. Derlenen sorgu, getNameById(long)
öğesinin farklı çağrıları arasında yeniden kullanılır.
Kodda değil SQL'de yineleme yapma
Tek tek sonuç döndürmek için SQL sorgularında yineleme yapan 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şlenmesi gereken veri miktarını azaltarak sorgularınızın performansını artırabilir. Örneğin, bir sütundan yalnızca benzersiz değerleri döndürmek istiyorsanız 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 olmadan 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ı getirmek için eşleşen bir satır yoksa EXISTS()
işlevini kullanarak 0
, bir veya daha fazla satır eşleşiyorsa 1
değerini döndürebilirsiniz:
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 toplama işlevlerini kullanın:
COUNT
: Bir sütunda kaç satır olduğunu sayar.SUM
: Bir sütundaki tüm sayısal değerleri toplar.MIN
veyaMAX
: En düşük ya da en yüksek değeri belirler. Sayısal sütunlar,DATE
türleri ve metin türleri için çalışır.AVG
: Sayısal değerin ortalamasını 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 iç içe yerleştirilmiş sorgular
SQL, birleştirilebilir ve alt sorguları, birleştirmeleri ve yabancı anahtar kısıtlamalarını destekler. Uygulama kodunu incelemeden bir sorgunun sonucunu başka bir sorguda kullanabilirsiniz. Bu, SQLite'ten veri kopyalama ihtiyacını azaltır ve veritabanı motorunun sorgunuzu optimize etmesine olanak tanır.
Aşağıdaki örnekte, en çok müşteriye sahip şehri bulmak için bir sorgu çalıştırabilir, ardından bu şehirden 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()) { ... } } } }
Önceki örneğin yarısı kadar sürede sonucu 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
Bir satırın, belirli bir sütun değeri tabloda benzersiz olmadığı sürece 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, satırı eklemek içinse 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, });
Kotlin veya Java'da benzersiz kısıtlamayı 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ğıdakilerle aynı işlemi yapar:
CREATE TABLE Customers(...);
CREATE UNIQUE INDEX CustomersUsername ON Customers(username);
Artık bir satır ekleyebilir ve kısıtlamayı SQLite'ın 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ü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 göre daha hızlı ve daha az ek yükle doğrular. Uygulama kodu yerine SQLite kullanmak en iyi uygulamadır.
Tek bir işlemde birden fazla ekleme işlemini toplu olarak gerçekleştirme
Bir işlem, birden fazla işlemi yürütür. Bu da yalnızca verimliliği değil, doğruluğu da artırır. Veri tutarlılığını artırmak ve performansı hızlandı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çmeye yardımcı olmak için aşağıdaki sorun giderme araçlarını sağlar.
SQLite'ın etkileşimli istemini kullanma
Sorgu çalıştırmak ve bilgi edinmek için makinenizde SQLite'ı çalıştırın.
Farklı Android platform sürümleri, SQLite'ın farklı düzeltmelerini kullanır. Android destekli bir cihazdaki motoru kullanmak için adb shell
kullanın ve hedef cihazınızda sqlite3
komutunu ç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 EXPLAIN QUERY PLAN
kullanarak bir sorguyu nasıl yanıtlayacağı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ı yapılması gerekir. Buna doğrusal karmaşıklık denir. SQLite'ın 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'tan 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 Analyzer
SQLite, performansla ilgili sorunları gidermek için kullanılabilecek ek bilgileri boşaltmak üzere sqlite3_analyzer
komut satırı arayüzü (KSA) sunar. Yüklemek için SQLite İndirme Sayfası'nı ziyaret edin.
adb pull
simgesini kullanarak hedef cihazdaki bir veritabanı dosyasını analiz için iş istasyonunuza indirebilirsiniz:
adb pull /data/data/<app_package_name>/databases/<db_name>.db
SQLite Tarayıcısı
Ayrıca, SQLite İndirmeler sayfasında SQLite Browser adlı GUI aracını da yükleyebilirsiniz.
Android günlük kaydı
Android, SQLite sorgularının süresini ölçer 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 izleme
Perfetto'yu yapılandırırken, tek tek sorgular için izlemeleri dahil etmek üzere aşağıdakileri ekleyebilirsiniz:
data_sources {
config {
name: "linux.ftrace"
ftrace_config {
atrace_categories: "database"
}
}
}
dumpsys meminfo
adb shell dumpsys meminfo <package-name>
, SQLite belleğiyle ilgili bazı ayrıntılar da dahil olmak üzere uygulamanın bellek kullanımıyla ilgili istatistikleri yazdırır. Örneğin, bu, bir geliştiricinin cihazında adb shell dumpsys meminfo com.google.android.gms.persistent
komutunun çıkışından alınmıştır:
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
DATABASES
bölümünde şunları bulabilirsiniz:
pgsz
: Bir veritabanı sayfasının boyutu (KB cinsinden).dbsz
: Veritabanının tamamının boyutu (sayfa olarak). Boyutu KB cinsinden almak içinpgsz
iledbsz
değerini çarpın.Lookaside(b)
: Bağlantı başına SQLite lookaside arabelleğine ayrılan bellek (bayt cinsinden). Bunlar genellikle çok küçüktür.cache hits
: SQLite, veritabanı sayfalarının önbelleğini tutar. Bu, sayfa önbelleği isabetlerinin sayısıdır (sayım).cache misses
: Sayfa önbelleği isabet etmeme sayısı (sayım).cache size
: Önbellekteki sayfa sayısı (adet). Boyutu KB cinsinden almak için bu sayıyıpgsz
ile çarpın.Dbname
: DB dosyasının yolu. Örneğimizde, aynı temel veritabanına birden fazla bağlantı olduğunu belirtmek için bazı veritabanlarının adlarına(1)
veya başka bir sayı eklenmiştir. İstatistikler bağlantı başına izlenir.
POOL STATS
bölümünde şunları bulabilirsiniz:
cache hits
: SQLite, hazırlanan ifadeleri önbelleğe alır ve SQL ifadelerini derlerken biraz çaba ve bellek tasarrufu sağlamak için sorguları çalıştırırken bunları yeniden kullanmaya çalışır. Bu, ifade önbelleği isabetlerinin sayısıdır.cache misses
: İfade önbelleği kaçırma sayısı (sayım).cache size
: Android 17'den itibaren bu, önbellekteki toplam hazırlanmış ifade sayısını listeler. Önceki sürümlerde bu değer, diğer iki sütunda listelenen isabet ve ıskaların toplamına eşittir ve önbellek boyutunu temsil etmez.
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir.
- Sürekli entegrasyonda karşılaştırma testleri çalıştırma
- Donmuş kare
- Makro karşılaştırma testi olmadan temel profiller oluşturma ve ölçme