Zrozum odczyty i zapisy na dużą skalę

Przeczytaj ten dokument, 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, zamiast tego zapoznaj się z przewodnikiem szybkiego startu .

Cloud Firestore to elastyczna, skalowalna baza danych do tworzenia urządzeń mobilnych, sieci i serwerów z Firebase i Google Cloud. Rozpoczęcie pracy z Cloud Firestore i pisanie bogatych i wydajnych aplikacji jest bardzo łatwe.

Aby mieć pewność, że Twoje aplikacje będą nadal działać dobrze w miarę wzrostu rozmiaru bazy danych i wzrostu ruchu, pomocne jest zrozumienie mechaniki odczytu i zapisu w zapleczu Cloud Firestore. Należy także zrozumieć interakcję odczytu i zapisu z warstwą pamięci masowej oraz podstawowymi ograniczeniami, które mogą mieć wpływ na wydajność.

Przed zaprojektowaniem aplikacji zapoznaj się z poniższymi sekcjami, aby zapoznać się z najlepszymi praktykami.

Zrozumienie komponentów wysokiego poziomu

Poniższy diagram przedstawia komponenty wysokiego poziomu zaangażowane w żądanie Cloud Firestore API.

Komponenty na wysokim poziomie

Cloud Firestore SDK i biblioteki klienckie

Cloud Firestore obsługuje pakiety SDK i biblioteki klienckie dla różnych platform. Chociaż aplikacja może wykonywać bezpośrednie wywołania HTTP i RPC do interfejsu API Cloud Firestore, biblioteki klienckie zapewniają warstwę abstrakcji, która upraszcza korzystanie z interfejsu API i wdraża najlepsze praktyki. Mogą również zapewniać dodatkowe funkcje, takie jak dostęp offline, pamięci podręczne i tak dalej.

Interfejs Google (GFE)

Jest to usługa infrastrukturalna wspólna dla wszystkich usług chmurowych Google. GFE przyjmuje przychodzące żądania i przekazuje je do odpowiedniej usługi Google (w tym kontekście usługi Firestore). Zapewnia także inne ważne funkcjonalności, w tym ochronę przed atakami typu Denial of Service.

Usługa Cloud Firestore

Usługa Cloud Firestore sprawdza żądanie API, co obejmuje uwierzytelnianie, autoryzację, kontrolę limitów i reguły bezpieczeństwa, a także zarządza transakcjami. Ta usługa Cloud Firestore obejmuje klienta pamięci masowej , który współdziała z warstwą pamięci w celu odczytu i zapisu danych.

Warstwa pamięci Cloud Firestore

Warstwa przechowywania Cloud Firestore jest odpowiedzialna za przechowywanie zarówno danych, jak i metadanych oraz powiązanych funkcji bazy danych udostępnianych przez Cloud Firestore. W poniższych sekcjach opisano sposób organizacji danych w warstwie przechowywania Cloud Firestore oraz sposób skalowania systemu. Poznanie sposobu organizacji danych może pomóc w zaprojektowaniu skalowalnego modelu danych i lepszym zrozumieniu najlepszych praktyk w Cloud Firestore.

Kluczowe zakresy i podziały

Cloud Firestore to baza danych NoSQL zorientowana na dokumenty. Dane przechowujesz w dokumentach zorganizowanych w hierarchie kolekcji . Hierarchia kolekcji i identyfikator dokumentu są tłumaczone na jeden klucz dla każdego dokumentu. Dokumenty są logicznie przechowywane i uporządkowane leksykograficznie za pomocą tego jednego klucza. Terminu zakres kluczy używamy w odniesieniu do leksykograficznie ciągłego zakresu kluczy.

Typowa baza danych Cloud Firestore jest zbyt duża, aby zmieścić się na pojedynczej maszynie fizycznej. Istnieją również scenariusze, w których obciążenie danymi jest zbyt duże, aby mogła je obsłużyć jedna maszyna. Aby obsłużyć duże obciążenia, Cloud Firestore dzieli dane na osobne części, które można przechowywać i udostępniać na wielu komputerach lub serwerach pamięci masowej . Partycje te tworzone są w tabelach bazy danych w blokach zakresów kluczowych zwanych podziałami.

Replikacja synchroniczna

Należy pamiętać, że baza danych jest zawsze replikowana automatycznie i synchronicznie. Podziały danych mają repliki w różnych strefach , dzięki czemu są dostępne nawet wtedy, gdy strefa stanie się niedostępna. Spójną replikacją do różnych kopii podziału zarządza algorytm Paxos zapewniający konsensus. Jedna replika każdego podziału jest wybierana do pełnienia funkcji lidera Paxos, który jest odpowiedzialny za obsługę zapisów w tym podziale. Replikacja synchroniczna daje Ci możliwość zawsze odczytania najnowszej wersji danych z Cloud Firestore.

Ogólnym rezultatem tego jest skalowalny i wysoce dostępny system, który zapewnia niskie opóźnienia zarówno w przypadku odczytu, jak i zapisu, niezależnie od dużego obciążenia i na bardzo dużą skalę.

Układ danych

Cloud Firestore to bezschematowa baza danych dokumentów. Jednak wewnętrznie układa dane głównie w dwóch tabelach w stylu relacyjnej bazy danych w swojej warstwie przechowywania w następujący sposób:

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

Poniższy diagram pokazuje, jak mogą wyglądać tabele bazy danych Cloud Firestore z podziałami. Podziały są replikowane w trzech różnych strefach, a każdy podział ma przypisanego lidera Paxos.

Układ danych

Pojedynczy region a wiele regionów

Podczas tworzenia bazy danych należy wybrać region lub wiele regionów .

Pojedyncza lokalizacja regionalna to określona lokalizacja geograficzna, np . USA-Zachód1 . Podziały danych bazy danych Cloud Firestore mają repliki w różnych strefach w wybranym regionie, jak wyjaśniono wcześniej.

Lokalizacja obejmująca wiele regionów składa się ze zdefiniowanego zestawu regionów, w których przechowywane są repliki bazy danych. W przypadku wieloregionowego wdrożenia Cloud Firestore dwa regiony mają pełne repliki całych danych w bazie danych. Trzeci region ma replikę monitora , która nie przechowuje pełnego zestawu danych, ale uczestniczy w replikacji. Dzięki replikacji danych między wieloma regionami dane można zapisywać i odczytywać nawet w przypadku utraty całego regionu.

Aby uzyskać więcej informacji na temat lokalizacji regionu, zobacz Lokalizacje Cloud Firestore .

Pojedynczy region a wiele regionów

Zrozum życie zapisu w Cloud Firestore

Klient Cloud Firestore może zapisywać dane, tworząc, aktualizując lub usuwając pojedynczy dokument. Zapisanie w pojedynczym dokumencie wymaga atomowej aktualizacji zarówno dokumentu, jak i powiązanych z nim wpisów indeksu w warstwie przechowywania. Cloud Firestore obsługuje również operacje atomowe składające się z wielu odczytów i/lub zapisów w jednym lub większej liczbie dokumentów.

W przypadku wszystkich rodzajów zapisów Cloud Firestore zapewnia właściwości ACID (atomowość, spójność, izolacja i trwałość) relacyjnych baz danych. Cloud Firestore zapewnia także możliwość serializacji , co oznacza, że ​​wszystkie transakcje wyglądają tak, jakby były wykonywane w kolejności seryjnej.

Kroki wysokiego poziomu w transakcji zapisu

Kiedy klient Cloud Firestore wysyła zapis lub zatwierdza transakcję przy użyciu dowolnej z metod wymienionych wcześniej, wewnętrznie jest to wykonywane jako transakcja odczytu i zapisu bazy danych w warstwie pamięci. Transakcja umożliwia Cloud Firestore zapewnienie wspomnianych wcześniej właściwości ACID.

Na pierwszym etapie transakcji Cloud Firestore odczytuje istniejący dokument i określa mutacje, jakie należy wprowadzić w danych w tabeli Dokumenty.

Obejmuje to również dokonanie niezbędnych aktualizacji tabeli Indeksy w następujący sposób:

  • Pola dodawane do dokumentów wymagają odpowiednich wstawek w tabeli Indeksy.
  • Pola usuwane z dokumentów wymagają odpowiedniego usunięcia w tabeli Indeksy.
  • Pola, które są modyfikowane w dokumentach wymagają zarówno usunięcia (w przypadku starych wartości), jak i wstawienia (w przypadku nowych wartości) w tabeli Indeksy.

Aby obliczyć wspomniane wcześniej mutacje, Cloud Firestore odczytuje konfigurację indeksowania dla projektu. Konfiguracja indeksowania przechowuje informacje o indeksach projektu. Cloud Firestore wykorzystuje dwa typy indeksów: jednopolowe i złożone. Aby uzyskać szczegółowe informacje na temat indeksów utworzonych w Cloud Firestore, zobacz Typy indeksów w Cloud Firestore .

Po obliczeniu mutacji Cloud Firestore zbiera je w ramach transakcji, a następnie zatwierdza.

Zrozumienie transakcji zapisu w warstwie pamięci

Jak wspomniano wcześniej, zapis w Cloud Firestore obejmuje transakcję odczytu i zapisu w warstwie pamięci. W zależności od układu danych zapis może obejmować jeden lub więcej podziałów, jak widać w układzie danych .

Na poniższym diagramie baza danych Cloud Firestore składa się z ośmiu podziałów (oznaczonych cyframi 1–8) hostowanych na trzech różnych serwerach pamięci masowej w jednej strefie, a każdy podział jest replikowany w 3 (lub więcej) różnych strefach. Każdy podział ma lidera Paxos, który może znajdować się w innej strefie w przypadku różnych podziałów.

Podział bazy danych Cloud Firestore

Rozważmy bazę danych Cloud Firestore zawierającą kolekcję Restaurants w następujący sposób:

Kolekcja restauracji

Klient Cloud Firestore żąda następującej zmiany w dokumencie w kolekcji Restaurant , aktualizując wartość pola priceCategory .

Zmień na dokument w kolekcji

Poniższe kroki wysokiego poziomu opisują, co dzieje się w ramach zapisu:

  1. Utwórz transakcję odczytu i zapisu.
  2. Przeczytaj dokument restaurant1 w kolekcji Restaurants z tabeli Dokumenty z warstwy przechowywania.
  3. Przeczytaj indeksy dokumentu z tabeli Indeksy .
  4. Oblicz mutacje, jakie należy wprowadzić w danych. W tym przypadku istnieje pięć mutacji:
    • M1: Zaktualizuj wiersz restaurant1 w tabeli Dokumenty , aby odzwierciedlić zmianę wartości pola priceCategory .
    • M2 i M3: Usuń wiersze starej wartości priceCategory w tabeli Indeksy dla indeksów malejących i rosnących.
    • M4 i M5: Wstaw wiersze nowej wartości priceCategory w tabeli Indeksy dla indeksów malejących i rosnących.
  5. Popełnij te mutacje.

Klient magazynu w usłudze Cloud Firestore wyszukuje podziały będące właścicielami kluczy wierszy, które mają zostać zmienione. Rozważmy przypadek, w którym Split 3 obsługuje M1, a Split 6 obsługuje M2-M5. Mamy do czynienia z transakcją rozproszoną, w której uczestniczą wszystkie te podziały. Splity uczestnika mogą obejmować także dowolny inny split, z którego dane zostały wcześniej odczytane w ramach transakcji odczytu i zapisu.

Poniższe kroki opisują, co dzieje się w ramach zatwierdzenia:

  1. Klient magazynu wystawia zatwierdzenie. Zatwierdzenie zawiera mutacje M1-M5.
  2. Uczestnikami tej transakcji są części 3 i 6. Na koordynatora wybiera się jednego z uczestników, np. Split 3. Zadaniem koordynatora jest upewnienie się, że transakcja zostanie zatwierdzona lub przerwana atomowo u wszystkich uczestników.
    • Za pracę wykonaną przez uczestników i koordynatorów odpowiadają repliki liderów tych podziałów.
  3. Każdy uczestnik i koordynator uruchamia algorytm Paxos ze swoimi replikami.
    • Lider uruchamia z replikami algorytm Paxos. Kworum zostaje osiągnięte, jeśli większość replik wyrazi ok to commit odpowiedzi liderowi.
    • Następnie każdy uczestnik powiadamia koordynatora, gdy jest gotowy (pierwsza faza dwufazowego zatwierdzenia). Jeśli którykolwiek z uczestników nie może zatwierdzić transakcji, cała transakcja aborts .
  4. Gdy koordynator będzie wiedział, że wszyscy uczestnicy, w tym on sam, są przygotowani, przekazuje wszystkim uczestnikom wynik accept transakcji (druga faza zatwierdzenia dwufazowego). Na tym etapie każdy uczestnik zapisuje decyzję o zatwierdzeniu w stabilnym magazynie, a transakcja zostaje zatwierdzona.
  5. Koordynator odpowiada klientowi magazynu w Cloud Firestore, że transakcja została zatwierdzona. Równolegle koordynator i wszyscy uczestnicy wprowadzają mutacje do danych.

Zatwierdź cykl życia

Gdy baza danych Cloud Firestore jest mała, może się zdarzyć, że pojedynczy podział będzie posiadał wszystkie klucze w mutacjach M1-M5. W takim przypadku w transakcji uczestniczy tylko jeden uczestnik i wspomniane wcześniej dwufazowe zatwierdzanie nie jest wymagane, co przyspiesza zapisy.

Zapisuje w wielu regionach

W przypadku wdrożeń obejmujących wiele regionów rozproszenie replik między regionami zwiększa dostępność, ale wiąże się z kosztem wydajności. Komunikacja między replikami w różnych regionach zajmuje więcej czasu w obie strony. Dlatego podstawowe opóźnienie operacji Cloud Firestore jest nieco większe w porównaniu do wdrożeń w jednym regionie.

Konfigurujemy repliki w taki sposób, aby przywództwo w przypadku podziałów zawsze pozostawało w regionie podstawowym. Region podstawowy to ten, z którego ruch przychodzący do serwera Cloud Firestore. Ta decyzja kierownictwa zmniejsza opóźnienia w obie strony w komunikacji między klientem magazynu w Cloud Firestore a liderem repliki (lub koordynatorem w przypadku transakcji typu multi-split).

Każdy zapis w Cloud Firestore wiąże się również z interakcją z silnikiem czasu rzeczywistego w Cloud Firestore. Aby uzyskać więcej informacji na temat zapytań w czasie rzeczywistym, zobacz temat Omówienie zapytań w czasie rzeczywistym na dużą skalę .

Zrozum życie odczytu w Cloud Firestore

W tej sekcji opisano samodzielne odczyty inne niż w czasie rzeczywistym w Cloud Firestore. Wewnętrznie serwer Cloud Firestore obsługuje większość tych zapytań w dwóch głównych etapach:

  1. Skanowanie pojedynczego zakresu w tabeli Indeksy
  2. Wyszukiwanie punktów w tabeli Dokumenty na podstawie wyniku wcześniejszego skanowania
Mogą istnieć pewne zapytania, które wymagają mniej lub więcej przetwarzania (na przykład zapytania IN) w Cloud Firestore.

Odczyty danych z warstwy przechowywania są wykonywane wewnętrznie przy użyciu transakcji bazy danych, aby zapewnić spójne odczyty. Jednak w przeciwieństwie do transakcji używanych do zapisu, transakcje te nie wymagają blokad. Zamiast tego działają, wybierając znacznik czasu, a następnie wykonując wszystkie odczyty w tym znaczniku czasu. Ponieważ nie uzyskują blokad, nie blokują współbieżnych transakcji odczytu i zapisu. Aby wykonać tę transakcję, klient magazynu w Cloud Firestore określa powiązany znacznik czasu, który informuje warstwę pamięci masowej, jak wybrać znacznik czasu odczytu. Typ sygnatury czasowej wybrany przez klienta magazynu w Cloud Firestore jest określony przez opcje odczytu dla żądania odczytu.

Zrozumienie transakcji odczytu w warstwie pamięci

W tej sekcji opisano typy odczytów i sposób ich przetwarzania w warstwie pamięci w Cloud Firestore.

Mocne odczyty

Domyślnie odczyty Cloud Firestore są bardzo spójne . Ta silna spójność oznacza, że ​​odczyt Cloud Firestore zwraca najnowszą wersję danych, która odzwierciedla wszystkie zapisy, które zostały zatwierdzone aż do rozpoczęcia odczytu.

Odczyt pojedynczego podziału

Klient magazynu w Cloud Firestore wyszukuje podziały będące właścicielami kluczy wierszy do odczytania. Załóżmy, że musi dokonać odczytu z Splitu 3 z wcześniejszej sekcji . Klient wysyła żądanie odczytu do najbliższej repliki, aby zmniejszyć opóźnienia w obie strony.

W tym momencie mogą zaistnieć następujące przypadki, w zależności od wybranej repliki:

  • Żądanie odczytu trafia do repliki lidera (strefa A).
    • Ponieważ lider jest zawsze na bieżąco, odczyt może być kontynuowany bezpośrednio.
  • Żądanie odczytu trafia do repliki innej niż lider (np. Strefa B)
    • Split 3 może wiedzieć na podstawie swojego stanu wewnętrznego, że ma wystarczającą ilość informacji do odczytu, a podział to robi.
    • Split 3 nie jest pewien, czy widział najnowsze dane. Wysyła wiadomość do lidera z prośbą o podanie znacznika czasu ostatniej transakcji, którą musi zastosować, aby umożliwić odczyt. Po zastosowaniu tej transakcji odczyt może być kontynuowany.

Następnie Cloud Firestore zwraca odpowiedź swojemu klientowi.

Odczyt z wieloma podziałami

W sytuacji, gdy odczyty muszą być wykonane z wielu podziałów, ten sam mechanizm zachodzi we wszystkich podziałach. Po zwróceniu danych ze wszystkich podziałów klient magazynu w Cloud Firestore łączy wyniki. Cloud Firestore następnie odpowiada swojemu klientowi tymi danymi.

Stal czyta

Silne odczyty to tryb domyślny w Cloud Firestore. Jednak wiąże się to z potencjalnymi większymi opóźnieniami wynikającymi z komunikacji, która może być wymagana z liderem. Często Twoja aplikacja Cloud Firestore nie musi czytać najnowszej wersji danych, a ta funkcjonalność działa dobrze w przypadku danych, które mogą być nieaktualne przez kilka sekund.

W takim przypadku klient może zdecydować się na otrzymywanie nieaktualnych odczytów, korzystając z opcji odczytu read_time . W tym przypadku odczyty są wykonywane w momencie, w którym dane znajdowały się w read_time , a najbliższa replika prawdopodobnie już zweryfikowała, czy zawiera dane w określonym read_time . Aby uzyskać zauważalnie lepszą wydajność, rozsądną wartością nieaktualności jest 15 sekund. Nawet w przypadku nieaktualnych odczytów otrzymane wiersze są ze sobą spójne.

Unikaj hotspotów

Podziały w Cloud Firestore są automatycznie dzielone na mniejsze części, aby w razie potrzeby rozdzielić pracę związaną z obsługą ruchu na większą liczbę serwerów pamięci masowej lub gdy kluczowa przestrzeń się powiększy. Podziały utworzone w celu obsługi nadmiernego ruchu są zachowywane przez około 24 godziny, nawet jeśli ruch zniknie. Jeśli zatem występują powtarzające się skoki ruchu, podziały są utrzymywane i w razie potrzeby wprowadzane są kolejne podziały. Mechanizmy te pomagają bazom danych Cloud Firestore w automatycznym skalowaniu w przypadku rosnącego obciążenia ruchem lub rozmiaru bazy danych. Istnieją jednak pewne ograniczenia, o których należy pamiętać, jak wyjaśniono poniżej.

Dzielenie pamięci i obciążenia wymaga czasu, a zbyt szybkie zwiększanie ruchu może powodować duże opóźnienia lub błędy związane z przekroczeniem terminu, zwane powszechnie punktami aktywnymi , podczas dostosowywania się usługi. Najlepszą praktyką jest dystrybucja operacji w całym zakresie kluczy, przy jednoczesnym zwiększeniu ruchu w kolekcji w bazie danych z 500 operacjami na sekundę. Po tym stopniowym zwiększaniu ruchu zwiększaj ruch nawet o 50% co pięć minut. Proces ten nazywany jest regułą 500/50/5 i ustawia bazę danych w taki sposób, aby optymalnie skalowała się w celu spełnienia wymagań obciążenia.

Chociaż podziały są tworzone automatycznie wraz ze wzrostem obciążenia, Cloud Firestore może dzielić zakres kluczy tylko do momentu udostępnienia pojedynczego dokumentu przy użyciu dedykowanego zestawu zreplikowanych serwerów pamięci masowej. W rezultacie duża i trwała liczba jednoczesnych operacji na pojedynczym dokumencie może prowadzić do powstania hotspotu w tym dokumencie. Jeśli w pojedynczym dokumencie występują utrzymujące się duże opóźnienia, należy rozważyć modyfikację modelu danych w celu podzielenia lub replikowania danych w wielu dokumentach.

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

Inny szczególny przypadek hotspottingu ma miejsce, gdy klucz sekwencyjnie rosnący/malejący jest używany jako identyfikator dokumentu w Cloud Firestore i występuje znacznie duża liczba operacji na sekundę. Tworzenie większej liczby podziałów tutaj nie pomaga, ponieważ wzrost ruchu po prostu przenosi się do nowo utworzonego podziału. Ponieważ Cloud Firestore domyślnie automatycznie indeksuje wszystkie pola w dokumencie, takie ruchome punkty aktywne mogą być również tworzone w przestrzeni indeksu dla pola dokumentu, które zawiera sekwencyjnie rosnącą/malejącą wartość, np. znacznik czasu.

Pamiętaj, że postępując zgodnie z praktykami opisanymi powyżej, Firestore może skalować się w celu obsługi dowolnie dużych obciążeń bez konieczności dostosowywania jakiejkolwiek konfiguracji.

Rozwiązywanie problemów

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

Co dalej