Informacje o zapytaniach w czasie rzeczywistym na dużą skalę

W tym dokumencie znajdziesz wskazówki dotyczące skalowania aplikacji bezserwerowej, która obsługuje więcej niż tysiące operacji na sekundę lub setki tysięcy użytkowników jednocześnie. Ten dokument zawiera zaawansowane tematy, które pomogą Ci dogłębnie zrozumieć 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 do internetu zapewniają zaawansowany model tworzenia aplikacji bezserwerowych, w których kod po stronie klienta ma bezpośredni dostęp do bazy danych. Pakiety SDK umożliwiają klientom nasłuchiwanie aktualizacji danych w czasie rzeczywistym. Możesz używać aktualizacji w czasie rzeczywistym do tworzenia aplikacji reagujących na zmiany, które nie wymagają infrastruktury serwerowej. Chociaż uruchomienie aplikacji jest bardzo proste, warto poznać ograniczenia systemów, które składają się na Cloud Firestore, aby aplikacja bezserwerowa skalowała się i działała dobrze przy zwiększonym ruchu.

W sekcjach poniżej znajdziesz wskazówki dotyczące skalowania aplikacji.

Wybierz lokalizację bazy danych w pobliżu użytkowników

Ten diagram przedstawia architekturę aplikacji działającej w czasie rzeczywistym:

Przykładowa architektura aplikacji działającej w czasie rzeczywistym

Gdy aplikacja działająca na urządzeniu użytkownika (mobilnym lub w internecie) nawiązuje połączenie z Cloud Firestore, jest ono kierowane do serwera front-end Cloud Firestore w tym samym regionie, w którym znajduje się Twoja baza danych. Jeśli na przykład baza danych jest w regionie us-east1, połączenie jest kierowane do frontendu Cloud Firestore, który również znajduje się w regionie us-east1. Te połączenia są długotrwałe i pozostają otwarte, dopóki aplikacja ich nie zamknie. Interfejs odczytuje dane z bazowych Cloud Firestore systemów pamięci masowej.

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 w Indiach, którego aplikacja komunikuje się z bazą danych w regionie Google Cloud w Ameryce Północnej, może zauważyć, że aplikacja działa wolniej i mniej płynnie niż w przypadku, gdyby baza danych znajdowała się bliżej, np. w Indiach lub w innej części Azji.

Projektowanie z myślą o niezawodności

Poniższe tematy wpływają na niezawodność aplikacji lub ją poprawiają:

Włączanie trybu offline

Pakiety SDK Firebase zapewniają trwałość danych offline. Jeśli aplikacja na urządzeniu użytkownika nie może połączyć się z Cloud Firestore, nadal będzie można z niej korzystać, ponieważ będzie działać na podstawie danych zapisanych lokalnie w pamięci podręcznej. Zapewnia to dostęp do danych nawet wtedy, gdy użytkownicy mają problemy z połączeniem internetowym lub całkowicie tracą dostęp na kilka godzin lub dni. Więcej informacji o trybie offline znajdziesz w artykule Włączanie danych offline.

Automatyczne ponawianie prób

Pakiety SDK Firebase automatycznie ponawiają operacje i przywracają przerwane połączenia. Pomaga to uniknąć błędów przejściowych spowodowanych ponownym uruchomieniem serwerów lub problemami z siecią między klientem a bazą danych.

Wybór lokalizacji regionalnych i wieloregionalnych

Wybór między lokalizacjami regionalnymi a wieloregionalnymi wiąże się z kilkoma kompromisami. Główna różnica polega na sposobie replikacji danych. Ma to wpływ na gwarancje dostępności aplikacji. Instancja wieloregionowa zapewnia większą niezawodność obsługi i zwiększa trwałość danych, ale wiąże się z wyższymi kosztami.

Omówienie systemu zapytań w czasie rzeczywistym

Zapytania w czasie rzeczywistym, zwane też detektorami zrzutów dysku, umożliwiają aplikacji nasłuchiwanie zmian w bazie danych i otrzymywanie powiadomień o niskim opóźnieniu, gdy tylko dane ulegną zmianie. Aplikacja może uzyskać ten sam wynik, okresowo sprawdzając bazę danych pod kątem aktualizacji, ale często jest to 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 kolejnych sekcjach znajdziesz szczegółowe informacje o działaniu odbiorników migawek oraz opis sprawdzonych metod skalowania zapytań w czasie rzeczywistym przy zachowaniu wydajności.

Wyobraź sobie 2 użytkowników, którzy łączą się z Cloud Firestore za pomocą aplikacji do przesyłania wiadomości utworzonej przy użyciu jednego z pakietów SDK na urządzenia mobilne.

Klient A zapisuje w bazie danych informacje o dodawaniu i aktualizowaniu dokumentów 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 aktualizacji w tej samej kolekcji za pomocą narzędzia do nasłuchiwania migawek. Klient B otrzymuje natychmiastowe powiadomienie za każdym razem, gdy ktoś utworzy nową wiadomość. Poniższy diagram przedstawia architekturę modułu nasłuchującego migawek:

Architektura połączenia detektora zrzutu

Gdy klient B połączy z bazą danych odbiornik migawek, nastąpi ta sekwencja zdarzeń:

  1. Klient B otwiera połączenie z Cloud Firestore i rejestruje odbiorcę, wywołując onSnapshot(collection("chatroom")) za pomocą pakietu SDK Firebase. Ten odbiornik może pozostawać aktywny przez wiele godzin.
  2. Cloud FirestoreFrontend wysyła zapytania do bazowego systemu pamięci masowej w celu zainicjowania zbioru danych. Wczytuje cały zbiór wyników pasujących dokumentów. Nazywamy to zapytaniem sprawdzającym. Następnie system sprawdza reguły zabezpieczeń Firebase bazy danych, aby potwierdzić, że użytkownik ma dostęp do tych danych. Jeśli użytkownik jest autoryzowany, baza danych zwraca mu dane.
  3. Zapytanie klienta B przechodzi wtedy w tryb nasłuchiwania. Detektor rejestruje się w programie obsługi subskrypcji i czeka na aktualizacje danych.
  4. Klient A wysyła teraz operację zapisu, aby zmodyfikować dokument.
  5. Baza danych zapisuje zmianę dokumentu w swoim systemie pamięci masowej.
  6. System zatwierdza tę samą aktualizację w wewnętrznym dzienniku zmian. Dziennik zmian określa ścisłą kolejność zmian w miarę ich wprowadzania.
  7. Dziennik zmian z kolei przekazuje zaktualizowane dane do puli modułów obsługi subskrypcji.
  8. Uruchamia się wyszukiwarka zapytań odwrotnych, aby sprawdzić, czy zaktualizowany dokument pasuje do jakichkolwiek zarejestrowanych obecnie detektorów migawek. W tym przykładzie dokument pasuje do odbiornika zrzutów klienta B. Jak sama nazwa wskazuje, można traktować dopasowywanie zapytań odwrotnych jako zwykłe zapytanie do bazy danych, ale wykonywane w odwrotnej kolejności. Zamiast przeszukiwać dokumenty w poszukiwaniu tych, które pasują do zapytania, skutecznie przeszukuje zapytania w poszukiwaniu tych, które pasują do przychodzącego dokumentu. Gdy system znajdzie pasujący dokument, przesyła go do detektorów zrzutów. Następnie system sprawdza reguły zabezpieczeń Firebase bazy danych, aby mieć pewność, że dane otrzymują tylko autoryzowani użytkownicy.
  9. System przekazuje aktualizację dokumentu do pakietu SDK na urządzeniu klienta B, a następnie wywoływana jest funkcja zwrotna onSnapshot. Jeśli lokalne utrwalanie jest włączone, pakiet SDK zastosuje aktualizację również w pamięci podręcznej.

Kluczowym elementem skalowalności Cloud Firestore jest zwielokrotnienie wyjściowe z dziennika zmian do modułów obsługi subskrypcji i serwerów frontendu. Rozsyłanie umożliwia efektywne rozpowszechnianie pojedynczej zmiany danych w celu obsługi milionów 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 w wielu regionach) Cloud Firestore osiąga wysoką dostępność i skalowalność.

Warto zauważyć, że wszystkie operacje odczytu wykonywane z poziomu pakietów SDK na urządzenia mobilne i do internetu są zgodne z powyższym modelem. Wykonują zapytanie o stan, a następnie przechodzą w tryb nasłuchiwania, aby zachować gwarancje spójności. Dotyczy to też detektorów w czasie rzeczywistym, wywołań służących do pobierania dokumentów i zapytań jednorazowych. Pojedyncze pobieranie dokumentów i zapytania jednorazowe można traktować jako krótkotrwałe odbiorniki zrzutów, które mają podobne ograniczenia dotyczące wydajności.

Stosuj sprawdzone metody skalowania zapytań w czasie rzeczywistym

Aby projektować skalowalne zapytania w czasie rzeczywistym, stosuj te sprawdzone metody.

Analizowanie dużego ruchu zapisu w systemie

Z tej sekcji dowiesz się, jak system reaguje na rosnącą liczbę żądań zapisu.

Cloud Firestore dzienniki zmian, które obsługują zapytania w czasie rzeczywistym, automatycznie skalują się w poziomie wraz ze wzrostem ruchu związanego z zapisem. Gdy szybkość zapisu w bazie danych przekracza możliwości jednego serwera, dziennik zmian jest dzielony na wiele serwerów, a przetwarzanie zapytań zaczyna korzystać z danych z wielu modułów obsługi subskrypcji zamiast z jednego. Z perspektywy klienta i pakietu SDK jest to w pełni przejrzyste i nie wymaga żadnych działań ze strony aplikacji w przypadku podziału. Poniższy diagram pokazuje, jak skalują się zapytania w czasie rzeczywistym:

Architektura zwielokrotnienia wyjściowego historii zmian

Automatyczne skalowanie umożliwia zwiększanie ruchu zapisu bez ograniczeń, ale w miarę wzrostu ruchu system może potrzebować trochę czasu na reakcję. Aby uniknąć tworzenia hotspotu zapisu, postępuj zgodnie z zaleceniami reguły 5-5-5. Key Visualizer to przydatne narzędzie do analizowania punktów zapisu.

Wiele aplikacji ma przewidywalny wzrost liczby bezpłatnych instalacji, który Cloud Firestore można uwzględnić bez środków ostrożności. Zadania wsadowe, takie jak importowanie dużego zbioru danych, mogą jednak zbyt szybko zwiększać liczbę zapisów. Podczas projektowania aplikacji pamiętaj, skąd pochodzi ruch związany z zapisem.

Jak interakcje zapisu i odczytu wpływają na siebie

System zapytań w czasie rzeczywistym możesz traktować jako potok łączący operacje zapisu z czytelnikami. Za każdym razem, gdy dokument zostanie utworzony, zaktualizowany lub usunięty, zmiana jest propagowana z systemu pamięci masowej do aktualnie zarejestrowanych odbiorców. Struktura dziennika zmian Cloud Firestore gwarantuje silną spójność, co oznacza, że aplikacja nigdy nie otrzymuje powiadomień o aktualizacjach, które są nieuporządkowane w porównaniu z momentem, w którym baza danych zatwierdziła zmiany danych. Upraszcza to tworzenie aplikacji, ponieważ eliminuje przypadki brzegowe związane ze spójnością danych.

Ten połączony potok oznacza, że operacja zapisu powodująca hotspoty lub spory o blokady może negatywnie wpływać na operacje odczytu. Gdy operacje zapisu nie powiodą się lub zostaną ograniczone, odczyt może się zatrzymać w oczekiwaniu na spójne dane z dziennika zmian. Jeśli tak się stanie w Twojej aplikacji, możesz zauważyć zarówno powolne operacje zapisu, jak i powiązane z nimi powolne czasy odpowiedzi na zapytania. Aby uniknąć tego problemu, należy unikać hotspotów.

Zadbaj o to, aby dokumenty i operacje zapisu były małe

Podczas tworzenia aplikacji z użyciem detektorów migawek zwykle chcesz, aby użytkownicy szybko dowiadywali się o zmianach danych. Aby to osiągnąć, staraj się ograniczać liczbę elementów. System może bardzo szybko przetwarzać małe dokumenty z dziesiątkami pól. Przetwarzanie większych dokumentów z setkami pól i dużą ilością danych trwa dłużej.

Podobnie preferuj krótkie i szybkie operacje zatwierdzania i zapisu, aby utrzymać niskie opóźnienie. Duże partie mogą z perspektywy autora zwiększyć przepustowość, ale w rzeczywistości mogą wydłużyć czas powiadomienia dla słuchaczy, którzy chcą tylko rzucić okiem na najnowsze informacje. W przypadku innych systemów baz danych, w których można używać przetwarzania wsadowego w celu zwiększenia wydajności, może to być sprzeczne z intuicją.

Używaj wydajnych odbiorników

Wraz ze wzrostem szybkości zapisu w bazie danych usługa Cloud Firestore rozdziela przetwarzanie danych na wiele serwerów. Algorytm dzielenia na partycje Cloud Firestorepróbuje umieścić dane z tej samej kolekcji lub grupy kolekcji na tym samym serwerze dziennika zmian. System stara się zmaksymalizować możliwą przepustowość zapisu, przy jednoczesnym utrzymaniu jak najmniejszej liczby serwerów zaangażowanych w przetwarzanie zapytania.

Niektóre wzorce mogą jednak nadal prowadzić do nieoptymalnego działania w przypadku odbiorców snapshot. Jeśli na przykład aplikacja przechowuje większość danych w jednej dużej kolekcji, odbiornik może potrzebować połączenia z wieloma serwerami, aby otrzymać wszystkie potrzebne dane. Dzieje się tak nawet wtedy, gdy zastosujesz filtr zapytania. Połączenie z wieloma serwerami zwiększa ryzyko wolniejszych odpowiedzi.

Aby uniknąć wolniejszych odpowiedzi, zaprojektuj schemat i aplikację tak, aby system mógł obsługiwać odbiorców bez konieczności przechodzenia do wielu różnych serwerów. Najlepiej podzielić dane na mniejsze kolekcje o mniejszej częstotliwości zapisu.

Można to porównać do zapytań dotyczących wydajności w relacyjnej bazie danych, które wymagają pełnego skanowania tabeli. W relacyjnej bazie danych zapytanie wymagające skanowania całej tabeli jest odpowiednikiem odbiornika migawek, który monitoruje kolekcję o dużej liczbie zmian. Może 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 jest podobne do odbiornika migawek, który obserwuje pojedynczy dokument lub kolekcję, która zmienia się rzadziej. Aby jak najlepiej poznać zachowanie aplikacji i potrzeby związane z Twoim przypadkiem użycia, przeprowadź test obciążeniowy.

Szybkie zapytania o stan

Kolejnym kluczowym elementem responsywnych zapytań w czasie rzeczywistym jest zapewnienie, że zapytanie o dane początkowe jest szybkie i wydajne. Gdy nowy odbiornik migawki po raz pierwszy nawiązuje połączenie, musi wczytać cały zestaw wyników i wysłać go na urządzenie użytkownika. Powolne zapytania sprawiają, że aplikacja działa mniej responsywnie. Dotyczy to na przykład zapytań, które próbują odczytać wiele dokumentów, lub zapytań, które nie używają odpowiednich indeksów.

W określonych okolicznościach detektor może też wrócić ze stanu nasłuchiwania do stanu odpytywania. Dzieje się to automatycznie i jest niewidoczne dla pakietów SDK i Twojej aplikacji. Stan odpytywania może być wywoływany przez te warunki:

  • System ponownie równoważy dziennik zmian ze względu na zmiany obciążenia.
  • Hotspoty powodują nieudane lub opóźnione zapisy w bazie danych.
  • Przejściowe ponowne uruchomienia serwera tymczasowo wpływają na słuchaczy.

Jeśli zapytania o stan są wystarczająco szybkie, stan ten staje się niewidoczny dla użytkowników aplikacji.

Preferowanie detektorów o długim czasie życia

Otwieranie i utrzymywanie aktywności odbiorników tak długo, jak to możliwe, jest często najbardziej opłacalnym sposobem tworzenia aplikacji korzystającej z Cloud Firestore. Gdy korzystasz z Cloud Firestore, płacisz za dokumenty zwrócone do aplikacji, a nie za utrzymywanie otwartego połączenia. Długotrwały odbiornik zrzutów odczytuje tylko dane potrzebne do obsługi zapytania przez cały okres jego istnienia. Obejmuje to początkowe sprawdzanie, a następnie powiadomienia o zmianach danych. Zapytania jednorazowe ponownie odczytują dane, które mogły się nie zmienić od czasu ostatniego wykonania zapytania przez aplikację.

W sytuacjach, w których aplikacja musi przetwarzać duże ilości danych, odbiorniki migawek mogą nie być odpowiednie. Jeśli na przykład Twój przypadek użycia wymaga przesyłania wielu dokumentów na sekundę przez połączenie przez dłuższy czas, lepiej będzie używać jednorazowych zapytań uruchamianych z mniejszą częstotliwością.

Co dalej?