Przeczytaj ten dokument, aby dowiedzieć się, jak skalować bezserwerową aplikację na potrzeby obsługi tysięcy operacji na sekundę lub setek tysięcy jednoczesnych użytkowników. Ten dokument zawiera zaawansowane tematy, które pomogą Ci dokładniej poznać system. Jeśli dopiero zaczynasz korzystać z Cloud Firestore, zapoznaj się z krótkim przewodnikiem.
Cloud Firestore i pakiety SDK Firebase na urządzenia mobilne i internet stanowią potężny model do tworzenia aplikacji bez serwera, w których kod po stronie klienta bezpośrednio uzyskuje dostęp do bazy danych. Pakiety SDK umożliwiają klientom odbieranie aktualizacji danych w czasie rzeczywistym. Aktualizacje w czasie rzeczywistym możesz wykorzystać do tworzenia responsywnych aplikacji, które nie wymagają infrastruktury serwerowej. Chociaż uruchomienie aplikacji bez serwera jest bardzo proste, warto poznać ograniczenia systemów, które ją tworzą, aby zapewnić jej skalowalność i dobrą wydajność przy zwiększonym natężeniu ruchu.Cloud Firestore
W następnych sekcjach znajdziesz porady dotyczące skalowania aplikacji.
Wybierz lokalizację bazy danych w pobliżu użytkowników.
Poniższy diagram przedstawia architekturę aplikacji działającej w czasie rzeczywistym:
Gdy aplikacja działająca na urządzeniu użytkownika (mobilnym lub internetowym) nawiązuje połączenie z Cloud Firestore, jest ono kierowane do serwera Cloud Firestore w tym samym regionie, w którym znajduje się Twoja baza danych. Jeśli na przykład baza danych znajduje się w us-east1
, połączenie jest też nawiązywane z frontendem Cloud Firestore, który też znajduje się w us-east1
. Te połączenia są długotrwałe i pozostają otwarte, dopóki aplikacja ich nie zamknie. Frontend odczytuje dane z podstawowych systemów pamięci masowej Cloud Firestore.
Odległość między fizyczną lokalizacją użytkownika a lokalizacją bazy danych Cloud Firestore wpływa na opóźnienie odczuwane przez użytkownika. Na przykład użytkownik z Indiek, którego aplikacja komunikuje się z bazą danych w regionie Google Cloud w Ameryce Północnej, może zauważyć wolniejsze działanie aplikacji i mniejsze jej tempo w porównaniu z tym, gdyby baza danych znajdowała się bliżej, na przykład w Indiach lub w innej części Azji.
Projektowanie z myślą o niezawodności
Poniższe tematy poprawiają niezawodność aplikacji lub wpływają na nią:
Włączanie trybu offline
Pakiety Firebase SDK zapewniają trwałość danych offline. Jeśli aplikacja na urządzeniu użytkownika nie może połączyć się z usługą Cloud Firestore, aplikacja nadal będzie działać, korzystając z danych w lokalnej pamięci podręcznej. Dzięki temu użytkownicy będą mieć dostęp do danych nawet wtedy, gdy będą mieć problemy z połączeniem z internetem lub całkowicie utracą dostęp na kilka godzin lub dni. Więcej informacji o trybie offline znajdziesz w artykule Włączanie danych offline.
Automatyczne próby
Pakiety SDK Firebase dbają o ponowne próby wykonania operacji i nawiązanie przerwanych połączeń. Pomaga to w omijaniu błędów przejściowych spowodowanych przez restartowanie serwerów lub problemy z siecią między klientem a bazą danych.
Wybierz lokalizację regionalną lub multiregionalną
Wybór między lokalizacjami regionalnymi a wieloregionalnymi wiąże się z kilkoma kompromisami. Główna różnica polega na sposobie replikowania danych. To zapewnia dostępność gwarantowaną przez aplikację. Instancji w wielu regionach zapewnia większą niezawodność wyświetlania i zwiększa trwałość danych, ale ma to swoją cenę.
System zapytań w czasie rzeczywistym
Zapytania w czasie rzeczywistym, zwane też odbiorcami zrzutów, umożliwiają aplikacji nasłuchiwanie zmian w bazie danych i otrzymywanie powiadomień z małą latencją, gdy tylko dane ulegną zmianie. Aplikacja może uzyskać ten sam wynik, okresowo sprawdzając bazę danych pod kątem aktualizacji, ale jest to często wolniejsze, droższe i wymaga więcej kodu. Przykłady konfigurowania i używania zapytań w czasie rzeczywistym znajdziesz w artykule Otrzymywanie aktualizacji w czasie rzeczywistym. W następnych sekcjach znajdziesz szczegółowe informacje o tym, jak działają odbiorcy zrzutów, oraz sprawdzone metody skalowania zapytań w czasie rzeczywistym przy zachowaniu wydajności.
Wyobraź sobie 2 użytkowników, którzy łączą się z usługą Cloud Firestore za pomocą aplikacji do obsługi wiadomości stworzonej za pomocą jednego z pakietów SDK na urządzenia mobilne.
Klient A zapisuje dane w bazie danych, aby dodać i zaktualizować dokumenty w kolekcji o nazwie chatroom
:
collection chatroom:
document message1:
from: 'Sparky'
message: 'Welcome to Cloud Firestore!'
document message2:
from: 'Santa'
message: 'Presents are coming'
Klient B nasłuchuje zmian w tej samej kolekcji za pomocą listenera migawka. Klient B otrzymuje natychmiastowe powiadomienie, gdy ktoś utworzy nową wiadomość. Poniższy diagram przedstawia architekturę odbiornika zrzutu:
Gdy klient B łączy słuchacza zrzutu z bazą danych, następuje ta sekwencja zdarzeń:
- Klient B otwiera połączenie z Cloud Firestore i rejestruje odsłuchiwanie, wykonując wywołanie funkcji
onSnapshot(collection("chatroom"))
za pomocą pakietu SDK Firebase. Ten odsłuch może trwać wiele godzin. - Frontend Cloud Firestore wysyła zapytania do podstawowego systemu magazynowania, aby zainicjować zbiór danych. Wczytuje cały zbiór wyników pasujących dokumentów. Nazywamy je zapytaniami pollingowymi. Następnie system sprawdza reguły zabezpieczeń Firebase w bazie danych, aby potwierdzić, że użytkownik ma dostęp do tych danych. Jeśli użytkownik jest autoryzowany, baza danych zwraca dane użytkownikowi.
- Zapytanie klienta B przechodzi wtedy w tryb słuchania. Listener rejestruje się w obsługującym go subskrypcji i czeka na aktualizacje danych.
- Klient A wysyła teraz operację zapisu, aby zmodyfikować dokument.
- Baza danych przekazuje zmiany dokumentu do systemu magazynowania.
- System przechowuje tę samą aktualizację w ramach transakcji w wewnętrznym changelogu. Lista zmian zawiera ścisłą kolejność wprowadzania zmian.
- Zmiany są następnie przesyłane do puli obsługi subskrypcji.
- Wykonywany jest odwrotny dopasowywanie zapytań, aby sprawdzić, czy zaktualizowany dokument pasuje do dowolnego z obecnie zarejestrowanych odbiorników zrzutu. W tym przykładzie dokument jest zgodny z odbiorcą zrzutu klienta B. Jak wskazuje nazwa, dopasowywacz zapytania odwrotnego można traktować jak zwykłe zapytanie do bazy danych, ale odwrotnie. Zamiast przeszukiwać dokumenty, aby znaleźć te, które pasują do zapytania, skutecznie przeszukuje zapytania, aby znaleźć te, które pasują do przychodzącego dokumentu. Po znalezieniu dopasowania system przekaże odpowiedni dokument odbiorcom zrzutów. Następnie system sprawdza reguły bezpieczeństwa Firebase bazy danych, aby mieć pewność, że dane otrzymają tylko autoryzowani użytkownicy.
- System przekazuje aktualizację dokumentu do pakietu SDK na urządzeniu klienta B, a wówczas wywołanie zwrotne
onSnapshot
zostaje wywołane. Jeśli trwałość lokalna jest włączona, pakiet SDK stosuje aktualizację również do lokalnego pamięci podręcznej.
Kluczowym elementem skalowalności Cloud Firestore jest rozgałęzienie od listy zmian do modułów obsługi subskrypcji i serwerów interfejsu. Rozgałęzienie umożliwia sprawne rozpowszechnianie jednej zmiany danych, aby obsługiwać miliony zapytań w czasie rzeczywistym i połączonych użytkowników. Dzięki uruchamianiu wielu replik wszystkich tych komponentów w wielu strefach (lub w wielu regionach w przypadku wdrożenia wieloregionowego) Cloud Firestore osiąga wysoką dostępność i skalowalność.
Warto pamiętać, że wszystkie operacje odczytu wydawane przez pakiety SDK na potrzeby urządzeń mobilnych i sieci opierają się na powyższym modelu. Aby zapewnić spójność, wykonują zapytanie o stan i potem przechodzą w tryb słuchania. Dotyczy to też odbiorców w czasie rzeczywistym, wywołań służących do pobierania dokumentu oraz zapytań jednorazowych. Wyszukiwanie pojedynczych dokumentów i jednorazowe zapytania można traktować jako krótkotrwałych słuchaczy zrzutów, którzy mają podobne ograniczenia dotyczące wydajności.
Stosowanie sprawdzonych metod skalowania zapytań w czasie rzeczywistym
Aby projektować skalowalne zapytania w czasie rzeczywistym, stosuj te sprawdzone metody.
Wysoki ruch zapisu w systemie
Z tej sekcji dowiesz się, jak system reaguje na rosnącą liczbę próśb o zapis.
Zmiany w Cloud Firestore, które powodują zapytania w czasie rzeczywistym, automatycznie zwiększają się w poziomie wraz ze wzrostem ruchu zapisującego. Gdy szybkość zapisu bazy danych przekroczy możliwości pojedynczego serwera, plik zmian zostanie podzielony na kilka serwerów, a przetwarzanie zapytań zacznie korzystać z danych z wielu modułów obsługi subskrypcji zamiast z jednego. Z perspektywy klienta i pakietu SDK jest to przejrzyste rozwiązanie. Gdy dochodzi do podziału, aplikacja nie musi podejmować żadnych działań. Na diagramie poniżej widać, jak skalują się zapytania w czasie rzeczywistym:
Automatyczne skalowanie umożliwia zwiększanie ruchu zapisu bez ograniczeń, ale wraz ze wzrostem natężenia ruchu system może potrzebować więcej czasu na reakcję. Aby uniknąć tworzenia hotspotów zapisu, stosuj się do zaleceń reguły 5-5-5. Key Visualizer to przydatne narzędzie do analizowania miejsc, w których najczęściej występują błędy.
Wiele aplikacji ma przewidywalny wzrost organiczny, który Cloud Firestore może uwzględnić bez konieczności podejmowania środków ostrożności. Zadania wsadowe, takie jak importowanie dużych zbiorów danych, mogą jednak powodować zbyt szybki wzrost liczby zapisów. Podczas projektowania aplikacji pamiętaj, skąd pochodzi ruch zapisu.
Jak działają zapisy i odczyty
System zapytań w czasie rzeczywistym można traktować jako potok łączący operacje zapisu z czytnikami. Za każdym razem, gdy dokument zostanie utworzony, zaktualizowany lub usunięty, zmiana jest rozpowszechniana z systemu magazynowania do zarejestrowanych w danej chwili odbiorców. Struktura pliku changelog w wersji Cloud Firestore zapewnia spójność, dzięki czemu Twoja aplikacja nigdy nie otrzymuje powiadomień o aktualizacjach, które są nieprawidłowe w stosunku do momentu zatwierdzenia zmian w bazie danych. Upraszcza to tworzenie aplikacji, ponieważ eliminuje przypadki szczególne związane z spójnością danych.
Połączony potok oznacza, że operacja zapisu powodująca problemy z wydajnością lub konflikty blokowania może negatywnie wpływać na operacje odczytu. Gdy operacje zapisu się nie udadzą lub zostaną ograniczone, odczyt może się zatrzymać, czekając na spójne dane z changelogu. Jeśli tak się stanie w Twojej aplikacji, możesz zauważyć zarówno spowolnione operacje zapisu, jak i powiązane z nimi spowolnione czasy odpowiedzi na zapytania. Unikanie obszarów hotspotów to klucz do unikania tego problemu.
Dokumenty i operacje zapisu powinny być małe.
Podczas tworzenia aplikacji z odbiornikami zrzutów ekranu zwykle zależy Ci na tym, aby użytkownicy szybko dowiadywali się o zmianach danych. Aby to osiągnąć, staraj się, aby elementy były małe. System może bardzo szybko przesyłać małe dokumenty z dziesiątkami pól. Przetwarzanie większych dokumentów z setkami pól i dużymi danymi trwa dłużej.
Z kolei, aby skrócić czas oczekiwania, preferuj krótkie i szybkie operacje zatwierdzania oraz zapisu. Duże partie mogą zapewnić większą przepustowość z punktu widzenia autora, ale mogą też wydłużyć czas powiadomienia dla odbiorców zrzutów. Jest to często nieintuicyjne w porównaniu z użyciem innych systemów baz danych, w których do poprawy wydajności można użyć grupowania.
Korzystanie z skutecznych odbiorców
Wraz ze wzrostem szybkości zapisu bazy danych Cloud Firestore rozdziela przetwarzanie danych na wiele serwerów. Algorytm partycjonowania Cloud Firestore próbuje umieszczać dane z tej samej kolekcji lub grupy kolekcji na tym samym serwerze zmian. System stara się zmaksymalizować możliwą przepustowość zapisu, utrzymując przy tym liczbę serwerów zaangażowanych w przetwarzanie zapytania na jak najniższym poziomie.
Niektóre wzorce mogą jednak nadal powodować nieoptymalne działanie w przypadku słuchaczy korzystających z migawek. Jeśli na przykład Twoja aplikacja przechowuje większość danych w jednej dużej kolekcji, odbiornik może potrzebować połączenia z wielu serwerami, aby otrzymać wszystkie potrzebne dane. Dotyczy to również sytuacji, gdy zastosujesz filtr zapytania. Łączenie się z wielu serwerów zwiększa ryzyko wolniejszego działania.
Aby uniknąć wolniejszych odpowiedzi, zaprojektuj schemat i aplikację tak, aby system mógł obsługiwać słuchaczy bez korzystania z wielu różnych serwerów. Najlepiej podzielić dane na mniejsze kolekcje o mniejszych szybkościach zapisu.
Jest to podobne do rozważania zapytań dotyczących wydajności w relacyjnej bazie danych, które wymagają pełnego skanowania tabeli. W relacyjnej bazie danych zapytanie, które wymaga pełnego skanowania tabeli, jest odpowiednikiem odbiorcy zrzutu, który obserwuje kolekcję o wysokiej rotacji. Może ono działać wolniej niż zapytanie, które baza danych może obsłużyć za pomocą bardziej szczegółowego indeksu. Zapytanie z bardziej szczegółowym indeksem działa jak odbiornik migawek, który monitoruje pojedynczy dokument lub kolekcję, która zmienia się rzadziej. Aby lepiej zrozumieć zachowanie i potrzeby związane z przypadkiem użycia, przeprowadź test wczytywania aplikacji.
Szybkie zapytania o odpytywanie
Kolejnym ważnym elementem zapytań w czasie rzeczywistym jest upewnienie się, że zapytanie pollingowe służące do inicjowania danych jest szybkie i wydajne. Gdy nowy odbiorca zrzutu łączy się po raz pierwszy, musi załadować cały zbiór wyników i przesłać go na urządzenie użytkownika. Powolne zapytania powodują, że aplikacja działa wolniej. Dotyczy to na przykład zapytań, które próbują odczytać wiele dokumentów, lub zapytań, które nie korzystają z odpowiednich indeksów.
W niektórych okolicznościach słuchacz może wrócić ze stanu słuchania do stanu sondowania. Dzieje się to automatycznie i nie wymaga interakcji ze strony SDK ani aplikacji. Stan odpytywania może być wywołany przez te warunki:
- System ponownie równoważy zmiany ze względu na zmiany obciążenia.
- Miejsca o wysokiej aktywności powodują nieudane lub opóźnione zapisy do bazy danych.
- Przejściowe restarty serwera mogą tymczasowo wpływać na słuchaczy.
Jeśli zapytania dotyczące ankiety są wystarczająco szybkie, stan ankiety staje się przejrzysty dla użytkowników aplikacji.
Preferuj słuchaczy o długim czasie trwania.
Otwarcie i utrzymanie słuchaczy przez jak najdłuższy czas jest często najbardziej opłacalnym sposobem tworzenia aplikacji korzystającej z funkcji Cloud Firestore. Gdy używasz interfejsu Cloud Firestore, płacisz za dokumenty zwrócone do aplikacji, a nie za utrzymywanie otwartego połączenia. Detektor zrzutu o długim czasie działania odczytuje tylko te dane, których potrzebuje do obsługi zapytania przez cały czas jego działania. Obejmuje to początkową operację odpytywania, po której następuje wysyłanie powiadomień, gdy dane ulegną zmianie. Zapytania jednorazowe natomiast ponownie odczytują dane, które mogły się nie zmienić od czasu ostatniego uruchomienia zapytania przez aplikację.
W przypadku, gdy aplikacja musi zużywać dużą ilość danych, odbiorcy zrzutów ekranu mogą nie być odpowiedni. Jeśli na przykład w Twoim przypadku przez dłuższy czas przesyłanych jest wiele dokumentów na sekundę, lepiej jest wybrać zapytania jednorazowe, które są wykonywane z mniejszą częstotliwością.
Co dalej
- Dowiedz się, jak używać odbiorców snapshot.
- Dowiedz się więcej o sprawdzonych metodach.