Jak tworzyć kopie zapasowe SQLite w S3 w locie bez utraty danych
Kochasz SQLite za jego prostotę? Wystaw świeży VPS, uruchom małą usługę, umieść plik bazy danych obok niej i wszystko działa z natychmiastowym czasem odpowiedzi. Żadnych dodatkowych kontenerów Docker z PostgreSQL, żadnego zarządzania użytkownikami czy konfiguracji praw dostępu. Piękne.
Problemy zaczynają się, gdy serwer niespodziewanie pada lub dostawca „przypadkowo" usuwa twój dysk. Jeśli tworzyłeś kopie zapasowe raz dziennie przez cron, straciłeś cały dzień użytkowników i zamówień. Jeśli próbowałeś skopiować plik bazy danych prostym cp podczas zapisywania, skończyłeś z uszkodzonym plikiem, którego SQLite nawet nie otworzy.
Twórca dobrze znanego key-value store'a BoltDB, Ben Johnson, stanął przed dokładnie tym dylematem i zbudował Litestream. To małe narzędzie w Go, które rozwiązuje problem kopii zapasowych SQLite raz na zawsze.
Co robi Litestream i dlaczego go potrzebujesz
Litestream działa jako proces w tle na tym samym serwerze co twoja aplikacja. Jego zadaniem jest ciągłe przesyłanie zmian z lokalnej bazy danych SQLite do zdalnego magazynu. Może to być chmura kompatybilna z S3, Yandex Object Storage, MinIO lub po prostu folder na innym zamontowanym dysku.
W rezultacie otrzymujesz gotowy system disaster recovery. Jeśli twój VPS pójdzie z dymem, wystawiasz nową instancję, uruchamiasz jedno polecenie przywracania i baza danych odzyskuje stan dokładnie sprzed ostatniej transakcji przed awarią.
Główną cechą narzędzia jest niezawodność. Komunikuje się z bazą danych ściśle przez oficjalne API C SQLite, a nie przez ślepe odczytywanie surowych bajtów z dysku. To gwarantuje, że fazy zapisu nie nakładają się na kompresję ani checkpointy dziennika, a baza danych nie zamieni się w uszkodzony plik.
Jak działa replikacja pod maską
Aby zrozumieć, jak to działa, przypomnijmy sobie tryb WAL (Write-Ahead Logging) w SQLite.
Domyślnie SQLite zapisuje zmiany bezpośrednio do głównego pliku .db. W trybie WAL wszystkie nowe transakcje trafiają najpierw do oddzielnego dziennika z rozszerzeniem -wal. Okresowo te zmiany są przelewane z powrotem do głównego pliku. To podejście przyspiesza zapisy i nie blokuje odczytów.
Litestream wykorzystuje ten mechanizm:
- Przełącza bazę w tryb WAL.
- Monitoruje pojawiające się nowe strony w pliku WAL.
- Kopiuje te zmiany i wysyła je małymi segmentami do S3.
- Kontroluje proces checkpointów, aby SQLite nie czyściło pliku WAL przedwcześnie, zanim dane zostaną przesłane do chmury.
Opóźnienie w wysyłaniu danych do S3 to zaledwie kilka sekund. Jeśli serwer padnie, maksimum, jakie tracisz, to dane z ostatnich kilku sekund.
Konfiguracja i przykład użycia
Replikację możesz uruchomić w ciągu kilku minut. Wystarczy pobrać binarkę lub użyć gotowego obrazu Docker.
Najpierw stwórzmy plik konfiguracyjny litestream.yml:
dbs:
- path: /var/lib/my-app/production.db
replicas:
- url: s3://my-backup-bucket/production.db
Parametry połączenia do S3, takie jak klucze dostępu i endpoint, przekazuje się zwykle przez standardowe zmienne środowiskowe: AWS_ACCESS_KEY_ID i AWS_SECRET_ACCESS_KEY.
Proces uruchamia się prostym poleceniem:
litestream replicate -config /etc/litestream.yml
Często Litestream działa w tym samym kontenerze Docker co główna aplikacja, używając lekkiego orchestratora procesów, takiego jak entrypoint.sh lub supervisord.
Jeśli serwer padł i musisz wdrożyć bazę danych w nowym miejscu, uruchamiasz polecenie restore przed startem aplikacji:
litestream restore -o /var/lib/my-app/production.db s3://my-backup-bucket/production.db
Util pobierze najnowszy bazowy zrzut z bucketa, ściągnie wszystkie brakujące segmenty WAL na wierzch, złoży kompletny plik bazy danych i umieści go w podanej ścieżce.
Ograniczenia i pułapki
Narzędzie wygląda bardzo atrakcyjnie, ale Litestream ma jasny zakres i swoje ograniczenia.
Po pierwsze, util wymaga włączenia trybu WAL. Jeśli twoja aplikacja lub konkretna biblioteka sterownika bazy danych jest z nim niekompatybilna, nic nie zadziała.
Po drugie, to narzędzie do disaster recovery, nie do horyzontalnego skalowania odczytów. Nie możesz użyć Litestream, żeby wystawić pięć kopii usługi na różnych serwerach czytających jednocześnie z tej samej zreplikowanej bazy danych. W scenariuszach rozproszonych z wieloma węzłami lepiej rozejrzyj się za LiteFS od tego samego zespołu lub projektami takimi jak rqlite.
Po trzecie, jeśli masz ogromną bazę danych liczącą setki gigabajtów z ciągłymi ciężkimi zapisami, ruch S3 i koszty żądań API mogą być niemiłym zaskoczeniem. Narzędzie jest zaprojektowane do małych i średnich obciążeń.
Kto znajdzie ten projekt przydatny
Litestream eliminuje potrzebę stawiania ciężkich baz danych tam, gdzie nie ma prawdziwej potrzeby. To doskonały wybór dla:
- Telegram botów i REST API w Go, Pythonie lub Node.js z pojedynczą instancją aplikacji.
- Projektów na frameworkach takich jak PocketBase czy Directus działających na SQLite.
- Osobistych usług self-hosted działających na tanich VPS.
- Mikroserwisów z lokalną pamięcią podręczną lub izolacją danych klientów.
Jeśli chciałeś przenieść swój projekt na SQLite, ale obawa przed utratą dysku cię powstrzymywała, spróbuj dodać Litestream. Konfiguracja zajmuje pół godziny, ale będziesz spać spokojnie.
Powiązane projekty