SQLite
 sql >> Baza danych >  >> RDS >> SQLite

Dostrajanie wydajności SQLite

SQLite to popularna, relacyjna baza danych, którą osadzasz w swojej aplikacji. Wraz ze wzrostem ilości danych w Twojej bazie danych musisz zastosować dostrajanie wydajności SQLite. W tym artykule omówiono indeksy i ich pułapki, użycie planera zapytań, tryb dziennika Write-Ahead-Logging (WAL) i zwiększanie rozmiaru pamięci podręcznej. Opisuje również znaczenie mierzenia wpływu twoich poprawek za pomocą automatycznych testów.

Wprowadzenie

SQLite to popularny system relacyjnych baz danych (DB) . W przeciwieństwie do swoich większych braci opartych na kliencie i serwerze, takich jak MySQL, SQLite może być osadzony w Twojej aplikacji jako biblioteka . SQLite ma bardzo podobny zestaw funkcji i może również obsługiwać miliony wierszy, biorąc pod uwagę, że znasz kilka wskazówek i wskazówek dotyczących dostrajania wydajności. Jak pokażą poniższe sekcje, jest więcej informacji na temat dostrajania wydajności SQLite niż tylko tworzenie indeksów.

Twórz indeksy, ale ostrożnie

Podstawową ideą indeksu jest przyspieszenie czytania określonych danych , czyli SELECT instrukcje z WHERE klauzula. Indeksy przyspieszają także sortowanie dane (ORDER BY ) lub JOIN stoły. Niestety, indeksy są mieczem obosiecznym, ponieważ zajmują dodatkowe miejsce na dysku i spowalniają manipulację danymi (INSERT , UPDATE , DELETE ).

Ogólna rada to tworzenie jak najmniejszej liczby indeksów, ale tak wielu, jak to konieczne . Ponadto indeksy mają sens tylko w przypadku większych bazy danych z tysiącami lub milionami wierszy.

Użyj planera zapytań do analizy zapytań

Sposób, w jaki indeksy są używane wewnętrznie przez SQLite, jest udokumentowany, ale niezbyt łatwy do zrozumienia. Jak szczegółowo omówiono w tym artykule, dobrym pomysłem jest analizowanie zapytania przez poprzedzenie go przedrostkiem EXPLAIN QUERY PLAN . Spójrz na każdą linię wyjściową, która ma trzy podstawowe warianty:

  • SEARCH table ... linie to dobry znak – SQLite używa jednego z Twoich indeksów!
  • SCAN table ... USING INDEX to zły znak,
  • SCAN table ... jest jeszcze gorszy!

Staraj się unikać SCAN table [using index] wpisy na wyjściu EXPLAIN QUERY PLAN w miarę możliwości, ponieważ napotkasz problemy z wydajnością na większych bazach danych. Użyj EXPLAIN QUERY PLAN do iteracyjnego dodawania lub modyfikowania indeksów, aż nie będzie więcej SCAN table pojawiają się wpisy.

Optymalizuj zapytania zawierające IS NOT

Sprawdzanie, czy IS NOT ... jest drogi ponieważ SQLite będzie musiał skanować wszystkie wiersze tabeli, nawet jeśli dana kolumna ma indeks . Indeksy są przydatne tylko wtedy, gdy szukasz konkretnych wartości, tj. porównań z udziałem < (mniejszy), (większe) lub = (równe), ale nie ubiegają się o !=(nierówne).

Sprytna sztuczka polega na tym, że możesz zastąpić WHERE column != value z WHERE column > value OR column < value . Użyje to indeksu kolumny i skutecznie wpłynie na wszystkie wiersze, których wartość nie jest równa value . Podobnie WHERE stringColumn != '' można zastąpić przez WHERE stringColumn > '' , ponieważ ciągi można sortować. Jednak stosując tę ​​sztuczkę, upewnij się, że wiesz, jak SQLite obsługuje NULL porównania. Na przykład SQLite oblicza NULL > '' jako FALSE .

Jeśli użyjesz takiej sztuczki porównawczej, istnieje jeszcze jedno zastrzeżenie na wypadek, gdyby zapytanie zawierało WHERE i ORDER BY , każda z inną kolumną:spowoduje to, że zapytanie znów będzie nieefektywne. Jeśli to możliwe, użyj tego samego kolumna w WHERE i ORDER BY lub zbuduj indeks pokrywający która obejmuje zarówno WHERE i ORDER BY kolumna.

Zwiększ szybkość zapisu dzięki zapisowi z wyprzedzeniem

Rejestracja zapisu z wyprzedzeniem (WAL) tryb dziennika znacznie poprawia wydajność zapisu/aktualizacji , w porównaniu z domyślnym wycofaniem tryb dziennika. Jednak, jak udokumentowano tutaj, jest kilka zastrzeżeń . Na przykład tryb WAL nie jest dostępny w niektórych systemach operacyjnych. Ponadto istnieją ograniczone gwarancje spójności danych w przypadku awarii sprzętu . Przeczytaj kilka pierwszych stron, aby zrozumieć, co robisz.

Odkryłem, że polecenie PRAGMA synchronous = NORMAL zapewnia 3-4-krotne przyspieszenie. Ustawianie journal_mode do WAL następnie znacznie poprawia wydajność (około 10x lub więcej, w zależności od systemu operacyjnego).

Oprócz zastrzeżeń, o których już wspomniałem, powinieneś również pamiętać o następujących kwestiach:

  • Korzystając z trybu dziennika WAL, obok pliku bazy danych w systemie plików będą znajdować się dwa dodatkowe pliki, które mają taką samą nazwę jak baza danych, ale mają przyrostek „-shm” i „-wal”. Zwykle nie musisz się tym przejmować, ale jeśli miałbyś wysłać bazę danych na inny komputer, gdy aplikacja jest uruchomiona, nie zapomnij dołączyć tych dwóch plików. SQLite skompaktuje te dwa pliki do głównego pliku za każdym razem, gdy zwykle zamykasz wszystkie otwarte połączenia z bazą danych.
  • Wydajność wstawiania lub aktualizowania sporadycznie spada, gdy zapytanie wyzwala scalanie zawartości pliku dziennika WAL z głównym plikiem bazy danych. Nazywa się to punktami kontrolnymi , zobacz tutaj.
  • Znalazłem, że PRAGMA s, które zmieniają journal_mode i synchronous nie wydają się być trwale przechowywane w bazie danych. Dlatego zawsze wykonuj je ponownie za każdym razem, gdy otwieram nowe połączenie z bazą danych, a nie tylko podczas tworzenia tabel po raz pierwszy.

Zmierz wszystko

Za każdym razem, gdy dodajesz poprawki wydajności, pamiętaj o zmierzeniu wpływu. Zautomatyzowane (jednostkowe) testy są do tego świetnym podejściem. Pomagają dokumentować Twoje ustalenia dla Twojego zespołu, a z czasem odkryją odmienne zachowania , np. po aktualizacji do nowszej wersji SQLite. Przykłady tego, co można zmierzyć:

  • Jaki jest efekt korzystania z WAL tryb dziennika po cofnięciu tryb? Jaki jest efekt innych (podobno) zwiększających wydajność PRAGMA tak?
  • O ile szybciej po dodaniu/zmienieniu/usunięciu indeksu wykonaj SELECT oświadczenia stają się? O ile wolniej robi INSERT/DELETE/UPDATE oświadczenia stają się?
  • Ile dodatkowego miejsca na dysku zajmują indeksy?

W przypadku dowolnego z tych testów rozważ powtórzenie ich z różnymi rozmiarami bazy danych. Np. uruchom je na pustej bazie danych, a także na bazie, która zawiera już tysiące (lub miliony) wpisów. Powinieneś także przeprowadzać testy na różnych urządzeniach i systemach operacyjnych, zwłaszcza jeśli Twoje środowisko programistyczne i produkcyjne znacznie się różnią.

Dostosuj rozmiar pamięci podręcznej

SQLite przechowuje tymczasowe informacje w pamięci podręcznej (w pamięci RAM), m.in. podczas budowania wyników SELECT zapytanie lub podczas manipulowania danymi, które nie zostały jeszcze zatwierdzone. Domyślnie ten rozmiar to marne 2 MB . Nowoczesne komputery stacjonarne mogą zaoszczędzić znacznie więcej. Uruchom PRAGMA cache_size = -kibibytes aby zwiększyć tę wartość (uwaga na minus znak przed wartością!). Więcej informacji znajdziesz tutaj. Ponownie zmierz jaki wpływ to ustawienie ma na wydajność!

Użyj REPLACE INTO, aby utworzyć lub zaktualizować wiersz

To może nie być tak duża zmiana wydajności, jak to zgrabna sztuczka. Załóżmy, że musisz zaktualizować wiersz w tabeli t lub utwórz wiersz, jeśli jeszcze nie istnieje. Zamiast używać dwóch zapytań (SELECT po którym następuje INSERT lub UPDATE ), użyj REPLACE INTO (oficjalna dokumentacja).

Aby to zadziałało, dodaj dodatkową fikcyjną kolumnę (np. replacer ) do tabeli t , który ma UNIQUE wymusić. Deklaracja kolumny mogłaby m.in. be ... replacer INTEGER UNIQUE ... która jest częścią twojego CREATE TABLE oświadczenie. Następnie użyj zapytania, takiego jak

REPLACE INTO t (col1, col2, ..., replacer) VALUES (?,?,...,1)Code language: SQL (Structured Query Language) (sql)

Kiedy to zapytanie zostanie uruchomione po raz pierwszy, po prostu wykona INSERT . Kiedy jest uruchamiany po raz drugi, UNIQUE ograniczenie replacer kolumna zostanie wyzwolona, ​​a zachowanie rozwiązywania konfliktów spowoduje porzucenie starego wiersza, automatycznie tworząc nowy. Przydatne może być również powiązane polecenie UPSERT.

Wniosek

Gdy liczba wierszy w Twojej bazie danych wzrośnie, poprawki wydajności stają się koniecznością. Indeksy to najczęstsze rozwiązanie. Zamieniają ulepszoną złożoność czasową na zmniejszoną złożoność przestrzenną, poprawiając prędkość odczytu, jednocześnie negatywnie wpływając na wydajność modyfikacji danych. Pokazałem, że przy porównywaniu nierówności należy zachować szczególną ostrożność w SELECT oświadczenia, ponieważ SQLite nie może używać indeksów do tego rodzaju porównań. Generalnie polecam korzystanie z planera zapytań to wyjaśnia, co dzieje się wewnętrznie dla każdego zapytania SQL. Za każdym razem, gdy coś zmieniasz, mierz wpływ!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. sqlite uzyskaj pole z ponad 2 MB

  2. Pula połączeń dla bazy danych „/data/data/msv_database.db” nie może nawiązać połączenia. Połączenia:0 aktywnych, 1 nieaktywnych, 0 dostępnych

  3. Jak Substr() działa w SQLite

  4. SQLiteOpenHelper:metoda onCreate() nie jest wywoływana na urządzeniu fizycznym

  5. Jak działa SQLite Upper()