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
veyaMAX
: 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"
}
}
}
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir
- Sürekli Entegrasyon'da karşılaştırmalar çalıştırma
- Donmuş kare
- Macrobenchmark olmadan Temel Profiller oluşturma ve ölçme