Jakie są cechy wydajności sqlite z bardzo dużymi plikami bazy danych? [Zamknięte]

325

Wiem, że sqlite nie radzi sobie dobrze z bardzo dużymi plikami bazy danych, nawet gdy są one obsługiwane (kiedyś na stronie sqlite pojawiał się komentarz, że jeśli potrzebujesz plików powyżej 1 GB, możesz rozważyć użycie rdbms dla przedsiębiorstw. już go nie znajdziesz, może być powiązany ze starszą wersją sqlite).

Jednak dla moich celów chciałbym dowiedzieć się, jak źle jest naprawdę, zanim rozważę inne rozwiązania.

Mówię o plikach danych sqlite w zakresie wielu gigabajtów, od 2 GB. Czy ktoś ma z tym jakieś doświadczenie? Wszelkie wskazówki / pomysły?

Snazzer
źródło
1
Używanie wątków (połączenie na wątek) może pomóc tylko w czytaniu - stackoverflow.com/a/24029046/743263
malkia
23
Rok 2016: Mam bazę danych 5 GB, która działa na SQLite bez żadnych problemów. Zainstalowałem dokładnie ten sam zestaw danych na Postgres. SQLite uruchomił złożone zapytanie w 2,7 ms, Postgres w 2,5 ms. Skończyłem na Postgres, aby uzyskać łatwiejszy dostęp do Regex i lepsze funkcje indeksowania. Ale byłem pod wrażeniem SQLite i mogłem go również użyć.
Paulb,

Odpowiedzi:

246

Więc zrobiłem kilka testów z sqlite dla bardzo dużych plików i doszedłem do pewnych wniosków (przynajmniej dla mojej konkretnej aplikacji).

Testy obejmują pojedynczy plik sqlite z jedną tabelą lub wieloma tabelami. Każda tabela miała około 8 kolumn, prawie wszystkie liczby całkowite i 4 indeksy.

Pomysł polegał na wstawieniu wystarczającej ilości danych, tak aby pliki sqlite miały rozmiar około 50 GB.

Pojedynczy stół

Próbowałem wstawić wiele wierszy do pliku sqlite za pomocą tylko jednej tabeli. Gdy plik miał około 7 GB (przepraszam, nie mogę sprecyzować liczby wierszy) wstawianie trwało zbyt długo. Oszacowałem, że mój test wstawienia wszystkich moich danych zajmie około 24 godzin, ale nie zakończył się nawet po 48 godzinach.

To prowadzi mnie do wniosku, że pojedyncza, bardzo duża tabela sqlite będzie miała problemy z wstawieniami i prawdopodobnie innymi operacjami.

Myślę, że nie jest to zaskoczeniem, ponieważ tabela się powiększa, wstawianie i aktualizowanie wszystkich indeksów trwa dłużej.

Wiele tabel

Następnie spróbowałem podzielić dane według czasu na kilka tabel, po jednej na dzień. Dane oryginalnej tabeli 1 zostały podzielone na ~ 700 tabel.

Ta konfiguracja nie miała problemów z wstawieniem, nie trwało dłużej w miarę upływu czasu, ponieważ codziennie tworzona była nowa tabela.

Problemy z próżnią

Jak wskazał i_like_caffeine, polecenie VACUUM jest problemem, im większy jest plik sqlite. W miarę wykonywania większej liczby operacji wstawiania / usuwania fragmentacja pliku na dysku będzie się pogarszać, dlatego celem jest okresowe VACUUM w celu optymalizacji pliku i odzyskania przestrzeni plików.

Jednak, jak wskazano w dokumentacji , powstaje pełna kopia bazy danych, aby wykonać próżnię, której wypełnienie zajmuje bardzo dużo czasu. Im mniejsza baza danych, tym szybciej zakończy się ta operacja.

Wnioski

W przypadku mojej konkretnej aplikacji prawdopodobnie podzielę dane na kilka plików db, jeden dziennie, aby uzyskać najlepszą wydajność próżni oraz szybkość wstawiania / usuwania.

To komplikuje zapytania, ale dla mnie warto zaindeksować tyle danych. Dodatkową zaletą jest to, że mogę po prostu usunąć cały plik db, aby upuścić dane o wartości dziennej (częste działanie mojej aplikacji).

Prawdopodobnie musiałbym również monitorować rozmiar tabeli dla pliku, aby zobaczyć, kiedy prędkość stanie się problemem.

Szkoda, że nie wydaje się być metodą przyrostową próżniowe inne niż próżni auto . Nie mogę go użyć, ponieważ moim celem dla próżni jest defragmentacja pliku (przestrzeń plików nie jest wielka sprawa), czego nie robi auto próżnia. W rzeczywistości dokumentacja mówi, że może to pogorszyć fragmentację, dlatego muszę okresowo robić pełną próżnię na pliku.

Snazzer
źródło
5
Bardzo przydatne informacje. Czysta spekulacja, ale zastanawiam się, czy nowego interfejsu tworzenia kopii zapasowych można używać do codziennego tworzenia niefragmentowanej wersji bazy danych i unikania konieczności uruchamiania VACUUM.
eodonohoe
24
Ciekawe, czy wszystkie twoje WSTAWKI były w transakcji?
Paul Lefebvre
9
Tak, wstawki zostały wykonane partiami po 10000 komunikatów na transakcję.
Snazzer
6
Z jakiego systemu plików korzystałeś? Jeśli ext {2,3,4}, jakie było ustawienie data =, czy włączono kronikowanie? Oprócz wzorców io sposób, w jaki sqlite opróżnia dysk, może być znaczący.
Tobu
5
Testowałem głównie w systemie Windows, więc nie mogę komentować zachowania w systemie Linux.
Snazzer
169

Na naszej platformie korzystamy z DBS 50 GB +. bez skarg działa świetnie. Upewnij się, że robisz wszystko dobrze! Czy używasz predefiniowanych instrukcji? * SQLITE 3.7.3

  1. Transakcje
  2. Gotowe oświadczenia
  3. Zastosuj te ustawienia (zaraz po utworzeniu bazy danych)

    PRAGMA main.page_size = 4096;
    PRAGMA main.cache_size=10000;
    PRAGMA main.locking_mode=EXCLUSIVE;
    PRAGMA main.synchronous=NORMAL;
    PRAGMA main.journal_mode=WAL;
    PRAGMA main.cache_size=5000;

Mam nadzieję, że to pomoże innym, działa świetnie tutaj

Alex
źródło
22
Niedawno przetestowany z dbs w zakresie 160 GB, działa również świetnie.
Snazzer
10
Również PRAGMA main.temp_store = MEMORY;.
Vikrant Chaudhary
40
@Alex, dlaczego istnieją dwa PRAGMA main.cache_size = 5000 ;?
Jack
23
Nie stosuj na ślepo tych optymalizacji. W szczególności synchroniczny = NORMALNY nie jest odporny na awarie. Tj. Awaria procesu we właściwym czasie może uszkodzić bazę danych nawet przy braku awarii dysku. sqlite.org/pragma.html#pragma_synchronous
mpm
22
@Alex, czy możesz wyjaśnić te wartości oraz różnicę między nimi a wartościami domyślnymi?
4m1nh4j1
65

Stworzyłem bazy danych SQLite o wielkości do 3,5 GB bez zauważalnych problemów z wydajnością. Jeśli dobrze pamiętam, myślę, że SQLite2 mógł mieć pewne dolne limity, ale nie sądzę, że SQLite3 ma takie problemy.

Zgodnie ze stroną limitów SQLite maksymalny rozmiar każdej strony bazy danych wynosi 32 KB. A maksymalna liczba stron w bazie danych to 1024 ^ 3. Tak więc według mojej matematyki maksymalny rozmiar wynosi 32 terabajty. Myślę, że przekroczysz granice systemu plików, zanim uderzysz w SQLite!

Paul Lefebvre
źródło
3
W zależności od tego, jakie operacje wykonujesz, próbując usunąć 3000 wierszy w bazie danych 8G sqlite, zajmie to wystarczająco dużo czasu, aby zaparzyć niezły garnek francuskiej prasy, lol
benjaminz
4
@benjaminz, musisz robić to źle. Jeśli w jednej transakcji zapiszesz usunięcie 3 tys. Wierszy, powinno to być prawie natychmiastowe. Sam popełniłem ten błąd: usuwanie 10 000 wierszy jeden po drugim trwało 30 minut. Ale kiedy zapakowałem wszystkie instrukcje usuwania w jedną transakcję, zajęło to 5 sekund.
mvp,
55

Wiele powodów, dla których wykonanie wstawek zajęło> 48 godzin, wynika z indeksów. Niezwykle szybciej jest:

1 - Usuń wszystkie indeksy 2 - Wykonaj wszystkie wstawki 3 - Utwórz ponownie indeksy

użytkownik352992
źródło
23
To dobrze znane ... ale w przypadku długotrwałego procesu nie będziesz okresowo upuszczać swoich indeksów w celu ich odbudowania, szczególnie gdy będziesz ich pytał o pracę. Takie podejście jest stosowane, gdy db sqlite musi zostać odbudowany od zera, indeksy są tworzone po wykonaniu wszystkich wstawek.
Snazzer
24
@ Snazzer w podobnej sytuacji użyliśmy tabeli „akumulatora”: raz dziennie przenosiliśmy zgromadzone wiersze ze stołu akumulatorów do stołu głównego w ramach jednej transakcji. W razie potrzeby widok zadbał o przedstawienie obu stołów jako pojedynczego stołu.
CAFxX,
4
Inną opcją jest zachowanie indeksów, ale przed ich wstawieniem wstępnie posortuj dane w kolejności indeksów.
Steven Kryskalla
1
@StevenKryskalla, jak to się ma do upuszczania indeksów i ich odtwarzania? Jakieś linki, które znasz, zostały przetestowane?
mcmillab
1
@mcmillab To było lata temu, więc nie pamiętam wszystkich szczegółów ani statystyk testu porównawczego, ale myśląc intuicyjnie, wstawienie N losowo uporządkowanych elementów do indeksu zajmie O (NlogN), podczas gdy wstawienie N posortowanych elementów zajmie O (N ) czas.
Steven Kryskalla,
34

Oprócz zwykłej rekomendacji:

  1. Indeks upuszczenia dla wkładki luzem.
  2. Wstawianie / aktualizacje partii w dużych transakcjach.
  3. Dostrój bufor bufora / wyłącz dziennik / w PRAGMA.
  4. Użyj 64-bitowej maszyny (aby móc korzystać z dużej ilości pamięci podręcznej ™).
  5. [dodano lipiec 2014 r.] Używaj wspólnego wyrażenia tabelowego (CTE) zamiast uruchamiania wielu zapytań SQL! Wymaga wersji SQLite 3.8.3.

Nauczyłem się następujących rzeczy z mojego doświadczenia z SQLite3:

  1. Aby uzyskać maksymalną szybkość wstawiania, nie należy używać schematu z żadnym ograniczeniem kolumny. (Zmień stół później w razie potrzeby Nie możesz dodawać ograniczeń za pomocą ALTER TABLE).
  2. Zoptymalizuj swój schemat, aby przechowywać to, czego potrzebujesz. Czasami oznacza to rozkładanie tabel i / lub nawet kompresowanie / przekształcanie danych przed wstawieniem do bazy danych. Świetnym przykładem jest przechowywanie adresów IP jako (długich) liczb całkowitych.
  3. Jedna tabela na plik db - w celu zminimalizowania rywalizacji o blokadę. (Użyj ATTACH DATABASE, jeśli chcesz mieć pojedynczy obiekt połączenia.
  4. SQLite może przechowywać różne typy danych w tej samej kolumnie (dynamiczne pisanie), wykorzystaj to na swoją korzyść.

Pytanie / komentarz mile widziane. ;-)

Lester Cheung
źródło
1
Jaki wpływ wywiera „jedna tabela na plik db”? Brzmi interesująco. Czy uważasz, że miałoby to duże znaczenie, gdyby twój stół miał tylko 3 stoły i był budowany od zera?
Martin Velez,
4
@ Martin nienawidzi to mówić, ale odpowiedź brzmi: to zależy . Chodzi o podzielenie danych na możliwe do zarządzania rozmiary. W moim przypadku użytkowania zbieram dane z różnych hostów i robię raportowanie danych po fakcie, aby to podejście działało dobrze. Podział według daty / godziny, jak sugerują inni, powinien dobrze działać w przypadku danych obejmujących długi okres, jaki mógłbym sobie wyobrazić.
Lester Cheung
3
@Lester Cheung: Jeśli chodzi o twój drugi numer 1: Z mojego rozumienia na podstawie dokumentów i osobistych doświadczeń wynika, że ​​do dziś SQLite3 nie obsługuje dodawania ograniczeń z ALTER TABLE po utworzeniu tabeli. Jedynym sposobem dodania lub usunięcia ograniczeń z istniejących wierszy tabeli jest utworzenie nowej tabeli z pożądanymi cechami i skopiowanie wszystkich wierszy, co prawdopodobnie będzie znacznie wolniejsze niż wstawianie raz z ograniczeniami.
Mumbleskates,
3
@Widdershins masz absolutną rację - ZMIEŃ TABELĘ w SQLite nie pozwala na dodawanie ograniczeń. Nie wiem, co paliłem - zaktualizuję odpowiedź - dzięki.
Lester Cheung
Żadna z tych sugestii nie ma nic wspólnego z używaniem ogromnych plików db SQLite. Czy pytanie było edytowane od czasu przesłania tej odpowiedzi?
A. Rager
9

Myślę, że główne zarzuty dotyczące skalowania sqlite to:

  1. Zapis jednoprocesowy.
  2. Bez dublowania.
  3. Brak replikacji.
Nieznany
źródło
9

Mam bazę danych SQLite 7 GB. Wykonanie określonego zapytania z łączeniem wewnętrznym zajmuje 2,6 s. Aby to przyspieszyć, próbowałem dodać indeksy. W zależności od tego, które indeksy dodałem, czasami zapytanie spadło do 0,1 s, a czasem nawet do 7. Myślę, że problemem w moim przypadku było to, że jeśli kolumna jest bardzo zduplikowana, wówczas dodanie indeksu obniża wydajność :(

Mike Oxynormas
źródło
9
Dlaczego kolumna z wieloma duplikatami obniża wydajność (poważne pytanie)?
Martin Velez,
6
kolumna o niskiej liczności jest trudniejsza do indeksowania: stackoverflow.com/questions/2113181/…
metrix
9

W dokumentacji SQLite znajdowało się stwierdzenie, że praktyczny limit rozmiaru pliku bazy danych wynosi kilkadziesiąt GB: s. Było to głównie spowodowane koniecznością „przydzielania bitmapy brudnych stron” przez SQLite przy każdym rozpoczęciu transakcji. Zatem 256 bajtów pamięci RAM było wymaganych na każdy MB w bazie danych. Wstawienie do pliku DB o pojemności 50 GB wymagałoby dużego (2 ^ 8) * (2 ^ 10) = 2 ^ 18 = 256 MB pamięci RAM.

Ale od najnowszych wersji SQLite nie jest to już potrzebne. Przeczytaj więcej tutaj .

Alix Axel
źródło
25
Bardzo mi przykro, że muszę to zaznaczyć, ale 2^18tak naprawdę to tylko 256 K.
Gabriel Schreiber
7
@GabrielSchreiber to, a także fakt, że 50 GB nie jest (2 ^ 10) MB, to tylko 1 GB. Tak więc dla bazy danych 50 GB potrzebujesz 12,5 MB pamięci: (2 ^ 8) * (2 ^ 10) * 50
elipoultorak
8

Wystąpiły problemy z dużymi plikami sqlite podczas używania polecenia Vacuum.

Nie próbowałem jeszcze funkcji auto_vacuum. Jeśli spodziewasz się często aktualizować i usuwać dane, warto to sprawdzić.

eodonohoe
źródło