Android ma wbudowaną obsługę SQLite, i efektywną bazę danych SQL. Stosuj te sprawdzone metody, aby zoptymalizować działanie aplikacji i utrzymać jej szybkość i stabilność wraz ze wzrostem ilości danych. Korzystając z tych sprawdzonych metod, zmniejszasz także ryzyko napotykasz problemy z wydajnością, które są trudne do odtworzenia; i rozwiązuj problemy.
Aby szybciej osiągnąć skuteczność, przestrzegaj tych zasad:
Czytanie mniejszej liczby wierszy i kolumn: zoptymalizuj zapytania, aby pobierać tylko niezbędne dane. Minimalizuj ilość danych odczytywanych z bazy danych, ponieważ nadmierne pobieranie danych może mieć wpływ na wydajność.
Przekazywanie zadań do silnika SQLite: wykonywanie obliczeń, filtrowanie i sortowanie w ramach zapytań SQL. Korzystanie z silnika zapytań SQLite może znacznie poprawić wydajność.
Zmodyfikuj schemat bazy danych: zaprojektuj schemat bazy danych, by ułatwić SQLite tworzyć wydajne plany zapytań i reprezentacje danych. Prawidłowe indeksowanie tabel i optymalizuj ich struktury, aby zwiększyć wydajność.
Możesz też skorzystać z dostępnych narzędzi do rozwiązywania problemów, aby mierzyć: i wydajność bazy danych SQLite, ułatwiając identyfikację obszarów wymagających optymalizacji.
Zalecamy korzystanie z biblioteki Jetpack Room.
Konfigurowanie bazy danych pod kątem wydajności
Wykonaj czynności opisane w tej sekcji, aby skonfigurować bazę danych pod kątem optymalizacji i wydajność w SQLite.
Włączanie logowania z wyprzedzeniem
SQLite implementuje mutacje, dołączając je do logu, który od czasu do czasu kompaktowe do bazy danych. Nazywa się to logowaniem z wyprzedzeniem (WAL).
Włącz
zapisywanie logów z wyprzedzeniem
chyba że korzystasz z ATTACH
DATABASE
.
Rozluźnij tryb synchronizacji
W przypadku korzystania z WAL domyślnie każde zatwierdzenie generuje żądanie fsync
, aby zapewnić
dane docierają do dysku. Zwiększa to trwałość danych, ale spowalnia proces zatwierdzania.
SQLite udostępnia opcję kontroli synchronicznej
. Jeśli włączysz WAL, ustaw tryb synchroniczny na NORMAL
:
Kotlin
db.execSQL("PRAGMA synchronous = NORMAL")
Java
db.execSQL("PRAGMA synchronous = NORMAL");
Przy takim ustawieniu powiadomienie o zmianie może zostać zwrócone, zanim dane zostaną zapisane na dysku. Jeśli może nastąpić wyłączenie urządzenia, np. w przypadku utraty zasilania lub paniki w wyniku działania jądra systemu możesz utracić zatwierdzone dane. Jednak dzięki rejestrowaniu baza danych nie jest uszkodzona.
Jeśli tylko aplikacja ulegnie awarii, dane nadal będą przesyłane na dysk. W przypadku większości aplikacji to ustawienie zapewnia poprawę wydajności bez dodatkowych kosztów.
Definiowanie wydajnych schematów tabel
Aby zoptymalizować wydajność i zminimalizować zużycie danych, zdefiniuj wydajny schemat tabeli. SQLite tworzy efektywne plany zapytań i dane, co prowadzi do szybszego pobierania danych. W tej sekcji znajdziesz sprawdzone metody tworzenia schematów tabeli.
Weź pod uwagę INTEGER PRIMARY KEY
W tym przykładzie zdefiniuj i wypełnij tabelę w ten sposób:
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');
Dane wyjściowe tabeli będą wyglądać tak:
Rowid (identyfikator wiersza) | id | nazwa | miasto |
---|---|---|---|
1 | 456 | John Lennon | Liverpool, Anglia |
2 | 123 | Michaela Jacksona | Gary, Indiana |
3 | 789 | Wózek Parton | Sevier County, Tennessee |
Kolumna rowid
to
indeksu, który zachowuje zamówienie reklamowe. Zapytania z filtrem rowid
są implementowane jako szybkie wyszukiwanie w drzewie B, ale zapytania z filtrem id
są wykonywane jako wolne skanowanie tabeli.
Jeśli planujesz wyszukiwanie do id
, możesz uniknąć przechowywania
rowid
oznacza mniej danych w pamięci i ogólną
szybszej bazy danych:
CREATE TABLE Customers(
id INTEGER PRIMARY KEY,
name TEXT,
city TEXT
);
Twoja tabela wygląda teraz tak:
id | nazwa | miasto |
---|---|---|
123 | Michaela Jacksona | Gary, Indiana |
456 | John Lennon | Liverpool, Anglia |
789 | Wózek Parton | Sevier County, Tennessee |
Ponieważ nie musisz przechowywać kolumny rowid
, zapytania dotyczące id
są szybkie. Notatka
że tabela jest teraz posortowana według parametru id
, a nie zamówienia reklamowego.
Przyspieszanie zapytań za pomocą indeksów
Zastosowania SQLite
indeksy
aby przyspieszyć zapytania. Podczas filtrowania (WHERE
), sortowania (ORDER BY
) lub
agreguje (GROUP BY
) kolumnę, jeśli tabela ma dla niej indeks,
zapytania są przyspieszone.
W poprzednim przykładzie filtrowanie według pola city
wymaga przeskanowania całej tabeli:
SELECT id, name
WHERE city = 'London, England';
W przypadku aplikacji, która zawiera wiele zapytań dotyczących miast, możesz przyspieszyć te zapytania za pomocą indeksu:
CREATE INDEX city_index ON Customers(city);
Indeks jest implementowany jako dodatkowa tabela posortowana według kolumny indeksu i przypisana do rowid
:
miasto | rowid |
---|---|
Gary, Indiana | 2 |
Liverpool, Anglia | 1 |
Hrabstwo Sevier, Tennessee | 3 |
Zwróć uwagę, że koszt miejsca na dane w kolumnie city
jest teraz podwójny, ponieważ jest to
obecny zarówno w pierwotnej tabeli, jak i w indeksie. Ponieważ używasz
indeksu, koszt dodatkowego miejsca na dane jest wart korzyści w postaci szybszych zapytań.
Nie utrzymuj jednak indeksu, którego nie używasz, aby uniknąć płacenia za miejsce na dane, które nie przynosi korzyści w zakresie wydajności zapytań.
Tworzenie indeksów wielokolumnowych
Jeśli Twoje zapytania łączą wiele kolumn, możesz utworzyć indeksy wielokolumnowe, aby w pełni przyspieszyć zapytania. Możesz też użyć indeksu w kolumnie zewnętrznej i pozwolić, aby wyszukiwanie wewnątrz zostało wykonane jako skanowanie liniowe.
Na przykład w przypadku następującego zapytania:
SELECT id, name
WHERE city = 'London, England'
ORDER BY city, name
Możesz przyspieszyć zapytanie za pomocą indeksu wielokolumnowego w tej samej kolejności, określone w zapytaniu:
CREATE INDEX city_name_index ON Customers(city, name);
Jeśli jednak masz tylko indeks dotyczący bazy danych city
, kolejność zewnętrzna pozostaje bez zmian.
z przyspieszeniem, podczas gdy kolejność wewnętrzna wymaga skanowania liniowego.
Działa to też w przypadku zapytań z prefiksem. Na przykład indeks
ON Customers (city, name)
przyspiesza też filtrowanie, porządkowanie i grupowanie
według city
, ponieważ tabela indeksu dla indeksu wielokolumnowego jest posortowana według
dla określonych indeksów w podanej kolejności.
Rozważ WITHOUT ROWID
.
Domyślnie SQLite tworzy w tabeli kolumnę rowid
, w której rowid
jest wartością
domyślnie INTEGER PRIMARY KEY AUTOINCREMENT
. Jeśli masz już kolumnę, w której
jest INTEGER PRIMARY KEY
, ta kolumna zostanie aliasem domeny rowid
.
W przypadku tabel, które mają klucz podstawowy inny niż INTEGER
lub złożony
kolumny, rozważ WITHOUT
ROWID
.
Przechowuj małe dane jako BLOB
, a duże jako plik
Jeśli chcesz powiązać z wierszem duże dane, takie jak miniatura obrazu lub zdjęcie kontaktu, możesz je przechowywać w kolumnie BLOB
lub w pliku, a potem w kolumnie zapisać ścieżkę do pliku.
Pliki są zwykle zaokrąglane do 4 KB. W przypadku bardzo małych plików, gdzie
błąd zaokrąglania jest istotny, więc lepiej przechowywać je w
jako BLOB
. SQLite minimalizuje wywołania systemu plików i jest szybszy niż
bazowego systemu plików
ale nie zawsze tak jest.
Zwiększ wydajność zapytań
Aby poprawić wydajność zapytań w SQLite, postępuj zgodnie z tymi sprawdzonymi metodami, które pozwolą zminimalizować czas odpowiedzi i zmaksymalizować wydajność przetwarzania.
Odczytaj tylko potrzebne wiersze
Filtry pozwalają zawęzić wyniki przez określenie określonych kryteriów, np. zakres dat, lokalizacja lub nazwa. Limity pozwalają kontrolować liczbę wyników, które widzisz:
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()) { ... } }
Odczytuj tylko potrzebne kolumny
Unikaj zaznaczania zbędnych kolumn, ponieważ może to spowodować spowalniać zapytania i marnować zasoby. Zamiast tego wybierz tylko kolumny w ich wykorzystaniu.
W poniższym przykładzie wybierasz id
, name
i phone
:
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); ... } }
Wystarczy jednak kolumna name
:
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); ... } }
Parametrowanie zapytań za pomocą kart SQL, a nie konkatenacji ciągu znaków
Ciąg zapytania może zawierać parametr, który jest znany tylko w czasie działania, np. w następujący sposób:
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; } } }
W poprzednim kodzie każde zapytanie tworzy inny ciąg znaków, przez co nie można korzystać z pamięci podręcznej instrukcji. Każde wywołanie wymaga do skompilowania kodu SQLite
przed wykonaniem. Zamiast tego możesz zamienić argument id
na
parameter oraz
powiąż wartość za pomocą algorytmu selectionArgs
:
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; } } }
Teraz zapytanie można skompilować raz i zapisać w pamięci podręcznej. Skompilowane zapytanie jest ponownie wykorzystywane
między różnymi wywołaniami funkcji getNameById(long)
.
Wykonuj iteracje w SQL, a nie w kodzie
Użyj jednego zapytania, które zwraca wszystkie kierowane wyniki, zamiast wyników automatycznych zapętlanie zapytań SQL w celu zwrócenia poszczególnych wyników. Automatyzacja jest około 1000 razy wolniejsza niż pojedyncze zapytanie SQL.
Użyj wartości DISTINCT
w przypadku wartości unikalnych
Użycie słowa kluczowego DISTINCT
może zwiększyć skuteczność zapytań o
zmniejszając ilość danych do przetworzenia. Na przykład:
aby zwrócić tylko unikalne wartości z kolumny, użyj DISTINCT
:
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 ... } }
Gdy to możliwe, używaj funkcji agregacji
Używaj funkcji agregacji do agregacji wyników bez danych wierszy. Na przykład parametr ten kod sprawdza, czy istnieje co najmniej 1 pasujący wiersz:
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 ... } }
Aby pobrać tylko pierwszy wiersz, możesz użyć funkcji EXISTS()
, która zwróci wartość 0
, jeśli pasujący wiersz nie istnieje, i wartość 1
, jeśli pasuje co najmniej 1 wiersz:
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 ... } }
Używaj w kodzie aplikacji funkcji agregacji SQLite:
COUNT
: zlicza, ile wierszy znajduje się w kolumnie.SUM
: dodaje wszystkie wartości liczbowe w kolumnie.MIN
lubMAX
: określa najniższą lub najwyższą wartość. Działa w przypadku wartości liczbowych kolumny,DATE
typy i typy tekstu.AVG
: znajduje średnią wartość liczbową.GROUP_CONCAT
: łączy ciągi znaków za pomocą opcjonalnego separatora.
Użyj COUNT()
zamiast Cursor.getCount()
W
z tego przykładu,
Funkcja Cursor.getCount()
odczytuje wszystkie wiersze z bazy danych i zwraca wszystkie wartości wierszy:
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(); ... }
Jeśli jednak użyjesz metody COUNT()
, baza danych zwróci tylko
liczba:
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); ... }
Zapytania Nest zamiast kodu
SQL można komponować i obsługiwać podzapytania, złączenia oraz ograniczenia kluczy obcych. Wynik jednego zapytania możesz wykorzystać w innym bez konieczności korzystania z kodu aplikacji. Zmniejsza to potrzebę kopiowania danych z SQLite i umożliwia bazie danych do optymalizacji zapytania.
W poniższym przykładzie możesz uruchomić zapytanie, aby sprawdzić, w którym mieście klientów, użyj go w kolejnym zapytaniu, by znaleźć wszystkich klientów to miasto:
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()) { ... } } } }
Aby uzyskać wynik w połowie czasu w porównaniu z poprzednim przykładem, użyj pojedynczego zapytania SQL z zagnieżdżonymi instrukcjami:
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()) { ... } }
Sprawdzanie unikalności w SQL
Jeśli nie można wstawić wiersza, o ile dana wartość kolumny nie jest unikalna w tabeli, lepiej wykorzystać tę niepowtarzalność w formie kolumny. .
W poniższym przykładzie wykonywane jest jedno zapytanie w celu sprawdzenia, czy wiersz i drugi w celu wstawienia:
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, });
Zamiast sprawdzać unikalne ograniczenie w Kotlin lub Javie, możesz je sprawdzić SQL podczas definiowania tabeli:
CREATE TABLE Customers(
id INTEGER PRIMARY KEY,
name TEXT,
username TEXT UNIQUE
);
SQLite działa tak samo jak poniżej:
CREATE TABLE Customers(...);
CREATE UNIQUE INDEX CustomersUsername ON Customers(username);
Teraz możesz wstawić wiersz i pozwolić SQLite sprawdzić ograniczenie:
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 obsługuje unikalne indeksy z wieloma kolumnami:
CREATE TABLE table(...);
CREATE UNIQUE INDEX unique_table ON table(column1, column2, ...);
SQLite weryfikuje ograniczenia szybciej i przy mniejszym nakładzie pracy niż w Kotlin i Javie. w kodzie. Sprawdzoną metodą jest używanie SQLite zamiast kodu aplikacji.
Zbiorcze umieszczanie wielu wstawienia w jednej transakcji
Transakcja zatwierdza wiele operacji, przez co poprawia brak ale i jej poprawność. Aby poprawić spójność danych możesz zwiększyć skuteczność, możesz zbiorczo wstawiać:
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() }
Korzystanie z narzędzi do rozwiązywania problemów
SQLite udostępnia następujące narzędzia do rozwiązywania problemów, które pomagają mierzyć wydajność.
Użyj interaktywnego promptu SQLite
Uruchom SQLite na swoim komputerze, aby wykonywać zapytania i uczyć się.
Różne wersje platformy Androida używają różnych wersji SQLite. Aby użyć funkcji
w tym samym mechanizmie co w urządzeniu z Androidem, używaj funkcji adb shell
i
uruchom sqlite3
na urządzeniu docelowym.
Możesz poprosić SQLite o zapytania o czas:
sqlite> .timer on
sqlite> SELECT ...
Run Time: real ... user ... sys ...
EXPLAIN QUERY PLAN
Możesz poprosić SQLite o wyjaśnienie, jak zamierza odpowiedzieć na zapytanie, używając EXPLAIN QUERY PLAN
:
sqlite> EXPLAIN QUERY PLAN
SELECT id, name
FROM Customers
WHERE city = 'Paris';
QUERY PLAN
`--SCAN Customers
W poprzednim przykładzie znalezienie wszystkich klientów z Paryża wymaga skanowania całej tabeli bez indeksu. Jest to tzw. złożoność liniowa. SQLite musi odczytać ze wszystkich wierszy i zachowaj tylko te wiersze, które pasują do klientów z Paryża. Aby to naprawić, możesz dodać indeks:
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=?
Jeśli używasz interaktywnej powłoki, możesz poprosić SQLite, aby zawsze wyjaśniał plany zapytań:
sqlite> .eqp on
Więcej informacji: Planowanie zapytań.
SQLite Analyzer
SQLite zapewnia
sqlite3_analyzer
interfejsu wiersza poleceń (CLI) do pobierania dodatkowych informacji, które mogą być używane
i rozwiązywać problemy z wydajnością. Aby ją zainstalować, wejdź na
Strona pobierania SQLite.
Za pomocą adb pull
możesz pobrać plik bazy danych z urządzenia docelowego na
stacja robocza do analizy:
adb pull /data/data/<app_package_name>/databases/<db_name>.db
Przeglądarka SQLite
Możesz również zainstalować narzędzie GUI Przeglądarka SQLite w SQLite Strona Pobrane pliki
Logowanie w Androidzie
Android wysyła zapytania SQLite do użytkowników i rejestruje je za Ciebie:
# 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"
}
}
}
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy obsługa JavaScript jest wyłączona
- Uruchamianie testów porównawczych w trybie ciągłej integracji
- Zablokowane klatki
- Tworzenie i pomiar profili bazowych bez testu porównawczego