Odczyty i zapisy na dużą skalę

Zapoznaj się z tym dokumentem, aby podejmować świadome decyzje dotyczące projektowania aplikacji pod kątem wysokiej wydajności i niezawodności. Ten dokument zawiera zaawansowane tematy Cloud Firestore. Jeśli dopiero zaczynasz korzystać z Cloud Firestore, przeczytaj krótki przewodnik.

Cloud Firestore to elastyczna i skalowalna baza danych do tworzenia urządzeń mobilnych, stron internetowych i serwerów, która jest dostępna w Firebase i Google Cloud. Rozpoczęcie korzystania z Cloud Firestore oraz pisanie zaawansowanych i zaawansowanych aplikacji jest bardzo łatwe.

Jeśli chcesz mieć pewność, że Twoje aplikacje będą nadal działać dobrze, a także zwiększać rozmiar bazy danych i zwiększać ruch, warto poznać mechanikę odczytów i zapisów w backendzie Cloud Firestore. Musisz też zrozumieć interakcję odczytu i zapisu z warstwą pamięci masowej oraz poznać podstawowe ograniczenia, które mogą wpływać na wydajność.

Zanim zaprojektujesz architekturę swojej aplikacji, zapoznaj się z poniższymi sekcjami, aby poznać sprawdzone metody.

Omówienie komponentów ogólnych

Na diagramie poniżej widać ogólne komponenty uwzględnione w żądaniu do interfejsu API Cloud Firestore.

Komponenty wysokiego poziomu

Cloud Firestore biblioteki pakietu SDK i biblioteki klienta

Cloud Firestore obsługuje pakiety SDK i biblioteki klienta na różnych platformach. Aplikacja może wykonywać bezpośrednie wywołania HTTP i RPC do interfejsu API Cloud Firestore, ale biblioteki klienta zapewniają warstwę abstrakcji, która upraszcza korzystanie z interfejsu API i wdraża sprawdzone metody. Mogą one też zapewniać dodatkowe funkcje, takie jak dostęp offline, pamięci podręczne itp.

Google Front End (GFE)

Jest to usługa infrastruktury wspólna dla wszystkich usług Google Cloud. GFE akceptuje przychodzące żądania i przekazuje je do odpowiedniej usługi Google (w tym kontekście jest to usługa Cloud Firestore). Zapewnia też inne ważne funkcje, w tym ochronę przed atakami typu DoS.

Usługa Cloud Firestore

Usługa Cloud Firestore sprawdza żądanie do interfejsu API, w tym uwierzytelnianie, autoryzację, kontrole limitów i reguły zabezpieczeń, a także zarządza transakcjami. Ta usługa Cloud Firestore zawiera klienta pamięci masowej, który współpracuje z warstwą przechowywania danych przy odczytach i zapisach.

Cloud Firestore warstwa pamięci masowej

Warstwa pamięci masowej Cloud Firestore odpowiada za przechowywanie danych i metadanych, a także za powiązane funkcje bazy danych udostępniane przez usługę Cloud Firestore. W sekcjach poniżej opisujemy, jak dane są zorganizowane w warstwie pamięci Cloud Firestore i w jaki sposób skaluje się system. Poznanie sposobu porządkowania danych pomoże Ci zaprojektować skalowalny model danych i lepiej zrozumieć sprawdzone metody w usłudze Cloud Firestore.

Zakresy kluczy i podziały

Cloud Firestore to baza danych NoSQL zorientowana na dokumenty. Dane przechowujesz w dokumentach, które są uporządkowane w hierarchie kolekcji. Hierarchia kolekcji i identyfikator dokumentu są przekładane na jeden klucz dla każdego dokumentu. Przy użyciu tego jednego klucza dokumenty są logicznie przechowywane i uporządkowane leksykograficznie. Terminu „zakres kluczy” używamy w odniesieniu do leksykograficznie sąsiedniego zakresu kluczy.

Typowa baza danych Cloud Firestore jest zbyt duża, aby zmieścić się na jednym komputerze fizycznym. Zdarzają się też sytuacje, w których zbiór zadań związanych z danymi jest zbyt duży, aby 1 maszyna mogła je obsłużyć. Aby obsługiwać duże zbiory zadań, Cloud Firestore dzieli dane na osobne części, które można przechowywać na wielu maszynach lub serwerach pamięci masowej i na nich udostępniać. Te partycje są tworzone w tabelach bazy danych w blokach zakresów kluczy zwanych podziałami.

Replikacja synchroniczna

Pamiętaj, że baza danych jest zawsze replikowana automatycznie i synchronicznie. Podziały danych mają repliki w różnych strefach, dzięki czemu są one dostępne nawet wtedy, gdy strefa jest niedostępna. Spójną replikacją do różnych kopii podziału zarządza algorytm Paxos na potrzeby konsensusu. 1 replika każdego przydziału wybiera się na lidera Paxos, który odpowiada za obsługę zapisów na tym podziale. Replikacja synchroniczna zapewnia możliwość odczytu najnowszych wersji danych z usługi Cloud Firestore w każdej chwili.

Efektem tego działania jest skalowalny i wysoce dostępny system, który zapewnia niewielkie opóźnienia zarówno w przypadku odczytów, jak i zapisów, niezależnie od dużych obciążeń i bardzo dużej skali.

Układ danych

Cloud Firestore to nieschematyczna baza danych dokumentów. Jednak wewnętrznie układa dane głównie w 2 tabelach relacyjnych baz danych w warstwie pamięci masowej w ten sposób:

  • Tabela Dokumenty: w tej tabeli przechowywane są dokumenty.
  • Tabela Indeksy: w tej tabeli przechowywane są wpisy indeksu, które umożliwiają efektywne uzyskiwanie wyników i posortowane według wartości indeksu.

Na diagramie poniżej widać, jak mogą wyglądać tabele bazy danych Cloud Firestore z podziałami. podziały są powielane w 3 różnych strefach, a każdy z nich ma przypisanego lidera Paxos.

Układ danych

Jeden region a wiele regionów

Podczas tworzenia bazy danych musisz wybrać region lub wiele regionów.

Pojedyncza lokalizacja regionalna to określona lokalizacja geograficzna, np. us-west1. Jak wyjaśniono wcześniej, podziały danych bazy danych Cloud Firestore mają repliki w różnych strefach w wybranym regionie.

Lokalizacja obejmująca wiele regionów składa się ze zdefiniowanego zbioru regionów, w których przechowywane są repliki bazy danych. We wdrożeniu Cloud Firestore w wielu regionach 2 z nich mają pełne repliki wszystkich danych w bazie danych. Trzeci region ma replikę poświadczającą, która nie utrzymuje pełnego zbioru danych, ale uczestniczy w replikacji. Dzięki replikacji danych między wieloma regionami można je zapisywać i odczytywać nawet w przypadku utraty całego regionu.

Więcej informacji o lokalizacjach regionu znajdziesz w sekcji Cloud Firestore lokalizacji.

Jeden region a wiele regionów

Poznaj życie pisania w języku: Cloud Firestore

Klient Cloud Firestore może zapisywać dane przez tworzenie, aktualizowanie lub usuwanie pojedynczego dokumentu. Zapis w jednym dokumencie wymaga atomowej aktualizacji zarówno dokumentu, jak i powiązanych z nim wpisów w indeksie w warstwie składowania danych. Cloud Firestore obsługuje także operacje niepodzielne obejmujące wiele odczytów lub zapisów w co najmniej jednym dokumencie.

W przypadku wszystkich rodzajów zapisów Cloud Firestore udostępnia właściwości ACID (atomowość, spójność, izolację i trwałość) relacyjnych baz danych. Cloud Firestore zapewnia również serializowalność, co oznacza, że wszystkie transakcje są realizowane tak, jakby były zawarte w zleceniu seryjnym.

Ogólne kroki transakcji zapisu

Gdy klient Cloud Firestore uruchomi lub zatwierdzi transakcję przy użyciu dowolnej z wymienionych wcześniej metod, jest to wewnętrznie wykonywane jako transakcja odczytu i zapisu w bazie danych w warstwie pamięci. Dzięki tej transakcji Cloud Firestore może udostępniać wymienione wcześniej właściwości ACID.

W pierwszym kroku transakcji Cloud Firestore odczytuje istniejący dokument i określa mutacje, które mają zostać wprowadzone w danych w tabeli dokumentów.

Obejmuje to też wprowadzenie niezbędnych zmian w tabeli Indeksy w ten sposób:

  • Pola dodawane do dokumentów muszą zawierać odpowiednie wstawki w tabeli Indeksy.
  • Pola usuwane z dokumentów muszą zostać usunięte w tabeli Indeksy.
  • Pola, które są modyfikowane w dokumentach, wymagają usunięcia (w przypadku starych wartości) i wstawiania (w przypadku nowych wartości) w tabeli Indeksy.

Aby obliczyć mutacje wspomniane wcześniej, Cloud Firestore odczytuje konfigurację indeksowania dla projektu. Konfiguracja indeksowania zawiera informacje o indeksach projektu. Funkcja Cloud Firestore używa 2 typów indeksów: 1 pola i Złożonego. Szczegółowe informacje o indeksach utworzonych w Cloud Firestore znajdziesz w artykule Typy indeksów w Cloud Firestore.

Po obliczeniu mutacji Cloud Firestore gromadzi je w transakcji, a potem zatwierdza.

Zrozumienie transakcji zapisu w warstwie pamięci

Jak wspomnieliśmy wcześniej, zapis w Cloud Firestore obejmuje transakcję odczytu i zapisu w warstwie pamięci masowej. W zależności od układu danych zapis może obejmować co najmniej jeden podział, tak jak w układzie danych.

Na poniższym diagramie baza danych Cloud Firestore ma 8 części (oznaczonych jako 1–8) hostowanych na 3 różnych serwerach pamięci masowej w 1 strefie. Każdy podział jest zreplikowany w co najmniej 3 różnych strefach. Każdy przydział ma swojego lidera Paxos, który może znajdować się w innej strefie w zależności od rodzaju podziału.

<span class=Podział bazy danych Cloud Firestore">

Weźmy pod uwagę bazę danych Cloud Firestore, która ma kolekcję Restaurants w ten sposób:

Kolekcja restauracji

Klient Cloud Firestore żąda poniższej zmiany dokumentu w kolekcji Restaurant, aktualizując wartość pola priceCategory.

Zmień na dokument w kolekcji

Te ogólne kroki procesu opisują, co się dzieje podczas zapisu:

  1. Utwórz transakcję do odczytu i zapisu.
  2. Przeczytaj dokument restaurant1 w kolekcji Restaurants z tabeli Dokumenty z warstwy przechowywania.
  3. Z tabeli Indeksy odczytać indeksy dla dokumentu.
  4. Oblicza mutacje, które zostaną wprowadzone w danych. W tym przypadku występuje pięć mutacji:
    • M1: zaktualizuj wiersz pola restaurant1 w tabeli Dokumenty, aby odzwierciedlić zmianę wartości w polu priceCategory.
    • M2 i M3: usuń wiersze ze starą wartością priceCategory z tabeli Indeksy dla indeksów malejących i rosnących.
    • M4 i M5: wstaw wiersze z nową wartością parametru priceCategory z tabeli Indeksy, aby wyświetlić indeksy malejące i rosnące.
  5. Zatwierdź te mutacje.

Klient pamięci masowej w usłudze Cloud Firestore wyszukuje podziały, do których należą klucze wierszy do zmiany. Przeanalizujmy przypadek, w którym Podział 3 obsługuje M1, a Podział 6 – M2–M5. Występuje rozproszona transakcja, w której wszystkie te podziały są realizowane jako uczestnicy. Podziały uczestników może również obejmować dowolny inny podział, z którego dane zostały wcześniej odczytane w ramach transakcji odczytu i zapisu.

Co się dzieje w ramach zatwierdzenia:

  1. Klient pamięci masowej generuje zatwierdzenie. Zatwierdzenie zawiera mutacje M1–M5.
  2. Transakcje 3 i 6 są uczestnikami tej transakcji. Jako koordynator wybierany jest jeden z uczestników, np. Podział 3. Zadaniem koordynatora jest dopilnowanie, aby transakcja była zatwierdzana lub przerywana atomowo u wszystkich uczestników.
    • Repliki liderów tych podziałów odpowiadają za pracę wykonaną przez uczestników i koordynatorów.
  3. Każdy uczestnik i koordynator przeprowadza algorytm Paxos przy użyciu odpowiednich replik.
    • Z replikami lider stosuje algorytm Paxos. Kworum jest mierzona, jeśli większość replik udzieli odpowiedzi ok to commit dla repliki wiodącej.
    • Następnie każdy uczestnik powiadamia koordynatora o przygotowaniu (pierwsza faza zobowiązania dwuetapowego). Jeśli żaden z uczestników nie może zrealizować transakcji, cała transakcja jest aborts.
  4. Gdy koordynator wie, że wszyscy uczestnicy, w tym samego siebie, są przygotowani, informuje wszystkich uczestników o wyniku transakcji accept (druga faza zatwierdzenia dwufazowego). Na tym etapie każdy uczestnik zapisuje decyzję dotyczącą zatwierdzenia w postaci stabilnej pamięci masowej, a transakcja zostaje zatwierdzona.
  5. Koordynator odpowiada klientowi miejsca na dane w Cloud Firestore, że transakcja została zatwierdzona. Równolegle koordynator i wszyscy uczestnicy stosują mutacje do danych.

Cykl życia zatwierdzenia

Gdy baza danych Cloud Firestore jest mała, może się zdarzyć, że do jednego podziału należą wszystkie klucze w mutacji M1–M5. W takim przypadku w transakcji jest tylko 1 uczestnik, a wspomniane wcześniej zatwierdzenie dwufazowe nie jest wymagane, co przyspiesza zapisy.

Zapisywanie w wielu regionach

W przypadku wdrożenia w wielu regionach rozłożenie replik w różnych regionach zwiększa dostępność, ale wiąże się z kosztem wydajności. Komunikacja między replikami w różnych regionach trwa dłużej. W związku z tym podstawowy czas oczekiwania w przypadku operacji Cloud Firestore jest nieco dłuższy niż w przypadku wdrożeń w jednym regionie.

Konfigurujemy repliki w taki sposób, aby liderzy w przypadku podziałów zawsze pozostawali w regionie głównym. Region główny to ten, z którego ruch przychodzi do serwera Cloud Firestore. Taka decyzja kierownictwa ogranicza opóźnienie komunikacji między klientem pamięci masowej w regionie Cloud Firestore a liderem repliki (lub koordynatorem transakcji wielokrotnych).

Każdy zapis w usłudze Cloud Firestore wiąże się też z pewną interakcją z mechanizmem działania silnika w czasie rzeczywistym w interfejsie Cloud Firestore. Więcej informacji o zapytaniach w czasie rzeczywistym znajdziesz w artykule Omówienie zapytań w czasie rzeczywistym na dużą skalę.

Zrozumienie życia czytania w Cloud Firestore

W tej sekcji omawiamy niezależne odczyty nie w czasie rzeczywistym w usłudze Cloud Firestore. Wewnętrznie serwer Cloud Firestore obsługuje większość tych zapytań na 2 głównych etapach:

  1. Skanowanie jednego zakresu w tabeli Indeksy
  2. wyszukiwania punktów w tabeli Dokumenty na podstawie wyniku wcześniejszego skanowania,
. Pewne zapytania mogą wymagać mniej lub więcej przetwarzania (np. zapytania IN) w języku: Cloud Firestore.

Odczyty danych z warstwy pamięci masowej są wykonywane wewnętrznie za pomocą transakcji bazy danych w celu zapewnienia spójnych odczytów. Jednak w przeciwieństwie do transakcji używanych do zapisu, transakcje te nie podlegają blokadom. Zamiast tego wybierają sygnaturę czasową, a następnie wykonują wszystkie odczyty z tą sygnaturą czasową. Nie nakładają blokad, więc nie blokują równoczesnych transakcji odczytu i zapisu. Aby zrealizować tę transakcję, klient pamięci masowej w Cloud Firestore określa powiązanie z sygnaturą czasową, która informuje warstwę przechowywania, jak wybrać sygnaturę czasową odczytu. Typ powiązanej sygnatury czasowej wybrany przez klienta pamięci masowej w Cloud Firestore jest określany przez opcje odczytu żądania odczytu.

Omówienie transakcji odczytu w warstwie pamięci

Ta sekcja opisuje typy odczytów i sposób ich przetwarzania w warstwie pamięci masowej w Cloud Firestore.

Mocne lektury

Domyślnie odczyty z Cloud Firestoresilnie spójne. Ta silna spójność oznacza, że odczyt Cloud Firestore zwraca najnowszą wersję danych, która odzwierciedla wszystkie zaplanowane zapisy do momentu rozpoczęcia odczytu.

Pojedynczy odczyt

Klient pamięci masowej w Cloud Firestore wyszukuje podziały, do których należą klucze wierszy do odczytu. Załóżmy, że musi odczytać informacje z rozdziału 3 z wcześniejszej sekcji. Klient wysyła żądanie odczytu do najbliższej repliki, aby zmniejszyć czas oczekiwania w obie strony.

W tym momencie w zależności od wybranej repliki mogą wystąpić te przypadki:

  • Żądanie odczytu trafia do repliki wiodącej (strefa A).
    • Lider jest zawsze aktualny, więc zapis można kontynuować bezpośrednio.
  • Żądanie odczytu trafia do repliki innej niż replika wiodąca (na przykład do strefy B)
    • Podział 3 może ustalić ze swojego stanu wewnętrznego, że ma wystarczającą ilość informacji do obsłużenia odczytu, a podział na to pozwala.
    • Podział 3 nie ma pewności, czy ma najnowsze dane. Wysyła wiadomość do lidera z prośbą o podanie sygnatury czasowej ostatniej transakcji, która musi zostać zastosowana w celu wyświetlenia odczytu. Po jej zakończeniu odczyt może być kontynuowany.

Cloud Firestore zwraca odpowiedź klientowi.

Odczyt wielokrotny

W sytuacji, gdy odczyty muszą być wykonywane w ramach wielu podziałów, na każdym z nich działa ten sam mechanizm. Po zwróceniu danych ze wszystkich przydziałów klient miejsca na dane w Cloud Firestore łączy wyniki. Następnie Cloud Firestore przesyła te dane klientowi.

Nieaktualne odczyty

Silne odczyty to tryb domyślny w Cloud Firestore. Wiąże się to jednak z potencjalnie dłuższym czasem oczekiwania ze względu na komunikację, która może być wymagana z liderem. Często aplikacja Cloud Firestore nie musi odczytywać najnowszych wersji danych, a funkcje jej dobrze współpracują z danymi, które mogą być nieaktualne (kilka sekund).

W takim przypadku klient może zdecydować się na otrzymywanie nieaktualnych odczytów, używając opcji odczytu read_time. W tym przypadku odczyty są wykonywane, ponieważ dane były w lokalizacji read_time, a najbliższa replika prawdopodobnie już potwierdziła, że posiada dane w określonym read_time. Dla uzyskania zauważalnego wzrostu skuteczności 15 sekund to rozsądna wartość braku aktualizacji. Nawet w przypadku nieaktualnych odczytów wygenerowane wiersze są ze sobą zgodne.

Unikaj hotspotów

Podziały w tabeli Cloud Firestore są automatycznie dzielone na mniejsze części, aby w razie potrzeby lub gdy przestrzeń kluczowa się powiększa, rozłożenie ruchu związanego z obsługą na większą liczbę serwerów pamięci masowej. Podziały utworzone w celu obsługi nadmiernego ruchu są przechowywane przez około 24 godziny, nawet jeśli ruch zniknie. Jeśli więc następują cykliczne skoki ruchu, podziały zostają utrzymywane, a w razie potrzeby wprowadzamy więcej podziałów. Te mechanizmy ułatwiają bazom danych Cloud Firestore autoskalowanie przy rosnącym obciążeniu lub wielkości bazy danych. Istnieją jednak pewne ograniczenia, o których należy pamiętać, opisane poniżej.

Podział miejsca na dane i obciążenia wymaga czasu, a zbyt szybkie zwiększanie ruchu może powodować błędy związane z dużym czasem oczekiwania lub przekroczeniem terminu, często nazywane hotspotami, gdy usługa dostosowuje się do zmian. Sprawdzoną metodą jest rozłożenie operacji w obrębie zakresu kluczy, jednocześnie zwiększając ruch związany z kolekcją w bazie danych z liczbą 500 operacji na sekundę. Następnie co 5 minut zwiększaj ruch nawet o 50%. Ten proces jest nazywany regułą 500/50/5 i pozycjonuje bazę danych w taki sposób, aby optymalnie skalowała się w celu wykonania zadania.

Chociaż podziały są tworzone automatycznie przy rosnącym obciążeniu, Cloud Firestore może dzielić zakres kluczy tylko do momentu, gdy wyświetli pojedynczy dokument przy użyciu dedykowanego zestawu zreplikowanych serwerów pamięci masowej. W związku z tym duża i długotrwała liczba równoczesnych operacji na 1 dokumencie może doprowadzić do utworzenia ważnego obszaru na tym dokumencie. Jeśli w jednym dokumencie występują długotrwałe opóźnienia, rozważ zmianę modelu danych, aby podzielić lub zreplikować dane w wielu dokumentach.

Błędy rywalizacji występują, gdy wiele operacji próbuje jednocześnie odczytać lub zapisać ten sam dokument.

Kolejnym szczególnym przypadkiem stosowania hotspotów jest sytuacja, w której jako identyfikator dokumentu w Cloud Firestore jest używany klucz o kolejnym zwiększaniu/zmniejszaniu, przy czym liczba operacji jest bardzo duża na sekundę. Większa liczba podziałów nie pomaga, ponieważ gwałtowny wzrost ruchu po prostu przenosi się do nowo utworzonego podziału. Funkcja Cloud Firestore domyślnie indeksuje wszystkie pola w dokumencie, dlatego w przestrzeni indeksu mogą być też tworzone ruchome hotspoty dla pola dokumentu, które zawiera kolejno rosnącą lub malejącą wartość, np. sygnaturę czasową.

Korzystając z podanych wyżej metod, Cloud Firestore może być skalowany tak, aby obsługiwać dowolnie duże zadania bez konieczności dostosowywania żadnej konfiguracji.

Rozwiązywanie problemów

Cloud Firestore udostępnia Key Visualizer jako narzędzie diagnostyczne przeznaczone do analizowania wzorców użytkowania i rozwiązywania problemów z hotspotami.

Co dalej