Uwaga: poniżej mogą występować nieścisłości. Nauczyłem się o wielu z tych rzeczy, więc weź to ze szczyptą soli. To jest dość długie, ale możesz po prostu odczytać parametry, z którymi graliśmy, a następnie przejść do Wniosku na końcu.
Istnieje wiele warstw, w których możesz się martwić wydajnością zapisu SQLite:
Spojrzeliśmy na te wyróżnione pogrubioną czcionką. Konkretne parametry to
- Pamięć podręczna zapisu dysku. Nowoczesne dyski mają pamięć podręczną RAM, która służy do optymalizacji zapisu na dysku w stosunku do wirującego dysku. Po włączeniu tej opcji dane mogą być zapisywane w blokach poza kolejnością, więc jeśli nastąpi awaria, możesz otrzymać częściowo zapisany plik. Sprawdź ustawienie za pomocą hdparm -W / dev / ... i ustaw je za pomocą hdparm -W1 / dev / ... (aby je włączyć, i -W0, aby je wyłączyć).
- bariera = (0 | 1). Wiele komentarzy online mówiących: „jeśli uruchamiasz z barierą = 0, nie włączaj buforowania zapisu na dysku”. Dyskusję na temat barier można znaleźć na stronie http://lwn.net/Articles/283161/
- data = (dziennik | zamówiony | zapis zwrotny). Zajrzyj na http://www.linuxtopia.org/HowToGuides/ext3JournalingFilesystem.html, aby uzyskać opis tych opcji.
- zatwierdzenie = N. Informuje ext3, aby synchronizował wszystkie dane i metadane co N sekund (domyślnie 5).
- Pragnienie synchroniczne SQLite = ON | POZA. Po włączeniu SQLite zapewni, że transakcja zostanie „zapisana na dysk” przed kontynuowaniem. Wyłączenie tego powoduje, że inne ustawienia są w dużej mierze nieistotne.
- SQLite pragma cache_size. Kontroluje, ile pamięci zajmie SQLite na pamięć podręczną w pamięci. Wypróbowałem dwa rozmiary: jeden, w którym cała baza danych zmieściłaby się w pamięci podręcznej, i jeden, w którym pamięć podręczna była o połowę większa od wielkości bazy danych.
Przeczytaj więcej o opcjach ext3 w dokumentacji ext3 .
Przeprowadziłem testy wydajności dla wielu kombinacji tych parametrów. Identyfikator to numer scenariusza, o którym mowa poniżej.
Zacząłem od uruchomienia z domyślną konfiguracją na moim komputerze jak w scenariuszu 1. Scenariusz 2 jest tym, co uważam za „najbezpieczniejszy”, a następnie wypróbowałem różne kombinacje, w stosownych przypadkach / monitowane. Prawdopodobnie najłatwiej to zrozumieć na mapie, którą ostatecznie wykorzystałem:
Napisałem skrypt testowy, który przeprowadził wiele transakcji, z wstawkami, aktualizacjami i usunięciami, wszystkie na tabelach z tylko INTEGER, tylko TEXT (z kolumną id) lub mieszane. Uruchomiłem to kilka razy w każdej z powyższych konfiguracji:
Dolne dwa scenariusze to # 6 i # 17, które mają „pragma synchronous = off”, więc nic dziwnego, że były najszybsze. Kolejna grupa trzech to # 7, # 11 i # 19. Te trzy są podświetlone na niebiesko na „mapie konfiguracji” powyżej. Zasadniczo konfiguracja obejmuje pamięć podręczną zapisu na dysku, barierę = 0 i zestaw danych do czegoś innego niż „dziennik”. Zmiana zatwierdzenia między 5 sekund (# 7) a 60 sekund (# 11) wydaje się nie mieć większego znaczenia. W tych testach nie było chyba żadnej różnicy między danymi = uporządkowanymi a danymi = zapisywanie zwrotne, co mnie zaskoczyło.
Test mieszanej aktualizacji jest środkowym pikiem. Istnieje zestaw scenariuszy, które są wyraźnie wolniejsze w tym teście. Wszystkie z danymi = dziennik . W przeciwnym razie nie ma wiele między innymi scenariuszami.
Miałem kolejny test synchronizacji, w którym wykonano bardziej heterogeniczną mieszankę wstawek, aktualizacji i usunięć dla różnych kombinacji typów. Zajęło to dużo dłużej, dlatego nie umieściłem go na powyższej fabule:
Tutaj możesz zobaczyć, że konfiguracja zapisu zwrotnego (# 19) jest nieco wolniejsza niż w zamówionych (# 7 i # 11). Spodziewałem się, że zapis będzie nieco szybszy, ale być może zależy to od twoich wzorców zapisu, a może po prostu jeszcze nie czytałem wystarczająco dużo na ext3 :-)
Różne scenariusze były nieco reprezentatywne dla operacji wykonywanych przez naszą aplikację. Po wybraniu krótkiej listy scenariuszy przeprowadziliśmy testy czasowe z niektórymi naszymi automatycznymi zestawami testów. Były zgodne z powyższymi wynikami.
Wniosek
- Popełnić parametr wydawało się trochę zmienić, tak, że wyjeżdżamy na 5s.
- Idziemy z pamięcią podręczną zapisu na dysku, barierą = 0 i danymi = uporządkowanymi . Przeczytałem kilka rzeczy online, które uważały, że jest to zła konfiguracja, a inne, które wydawały się uważać, że powinno to być domyślne w wielu sytuacjach. Najważniejsze jest to, że podejmujesz świadomą decyzję, wiedząc, jakie kompromisy podejmujesz.
- Nie będziemy używać synchronicznej pragmy w SQLite.
- Ustawienie pragmy SQLite cache_size, aby baza danych zmieściła się w pamięci, poprawiła wydajność niektórych operacji, zgodnie z oczekiwaniami.
- Powyższa konfiguracja oznacza, że podejmujemy nieco większe ryzyko. Będziemy używać SQLite Backup API, aby zminimalizować ryzyko awarii dysku przy częściowym zapisie: robienie migawki co N minut i utrzymywanie ostatniego M. Testowałem ten interfejs API podczas testów wydajności i daje nam to pewność, że pójdziemy tą drogą.
- Gdybyśmy nadal chcieli więcej, moglibyśmy popatrzeć na zabawę z jądrem, ale poprawiliśmy wszystko bez wchodzenia tam.
Dzięki @Huygens za różne wskazówki i wskazówki.