Przeczytaj ten dokument, aby podejmować świadome decyzje dotyczące architektury aplikacji zapewniającej wysoką wydajność i niezawodność. Ten dokument zawiera zaawansowane tematy dotyczące Cloud Firestore. Jeśli dopiero zaczynasz korzystać z Cloud Firestore, zapoznaj się z krótkim przewodnikiem.
Cloud Firestore to elastyczna, skalowalna baza danych do tworzenia aplikacji mobilnych, internetowych i serwerowych z Firebase i Google Cloud. Rozpoczęcie pracy z Cloud Firestore i tworzenie bogatych, wydajnych aplikacji jest bardzo proste.
Aby mieć pewność, że Twoje aplikacje będą działać prawidłowo, gdy rozmiar bazy danych i ruch będą się zwiększać, warto poznać mechanizm odczytu i zapisu na Cloud Firestore backendzie. Musisz też zrozumieć, jak procesy odczytu i zapisu współdziałają z poziomem pamięci masowej oraz jakie ograniczenia mogą wpływać na wydajność.
Zanim zaczniesz projektować aplikację, zapoznaj się ze sprawdzonymi metodami opisanymi w następnych sekcjach.
Elementy ogólne
Na diagramie poniżej widać ogólne komponenty, które są używane w żądaniu interfejsu API Cloud Firestore.
Cloud Firestore Pakiety 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 interfejsu API Cloud Firestore, ale biblioteki klienta zapewniają warstwę abstrakcji, która upraszcza korzystanie z interfejsu API i wdrażanie sprawdzonych metod. Mogą też udostępniać dodatkowe funkcje, takie jak dostęp offline, pamięć podręczna 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 usługi przechowywania, który współpracuje z warstwą przechowywania w celu odczytu i zapisu danych.
Cloud Firestore warstwa pamięci
Warstwa pamięci Cloud Firestore odpowiada za przechowywanie danych i metadanych oraz powiązanych funkcji bazy danych udostępnianych przez Cloud Firestore. W następnych sekcjach opisujemy sposób organizacji danych na warstwie pamięci Cloud Firestore oraz skalowanie systemu. Poznanie sposobu organizacji danych może pomóc Ci zaprojektować skalowalny model danych i lepiej zrozumieć sprawdzone metody w Cloud Firestore.
Kluczowe zakresy i podziały
Cloud Firestore to baza danych NoSQL zorientowana na dokumenty. Dane są przechowywane w dokumentach, które są uporządkowane w hierarchii kolekcji. Hierarchia kolekcji i identyfikator dokumentu są przekształcane w pojedynczy klucz dla każdego dokumentu. Dokumenty są logicznie przechowywane i uporządkowane alfabetycznie według tego klucza. Używamy terminu zakres kluczy, aby określić ciągły zakres kluczy uporządkowanych alfabetycznie.
Typowa baza danych Cloud Firestore jest zbyt duża, aby zmieścić się na jednym komputerze fizycznym. Są też sytuacje, w których obciążenie związane z danymi jest zbyt duże, aby pojedyncza maszyna mogła je obsłużyć. Aby obsługiwać duże obciążenia, Cloud Firestore dzieli dane na osobne części, które można przechowywać i przekazywać z wielu maszyn lub serwerów pamięci masowej. 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. Kopie danych mają repliki w różnych strefach, aby były dostępne nawet wtedy, gdy dana strefa stanie się niedostępna. Spójną replikacją do różnych kopii podziału zarządza algorytm Paxos na potrzeby konsensusu. Jedna replika każdej części jest wybierana jako lider Paxos, który odpowiada za obsługę zapisów w tej części. Replikacja synchroniczna zapewnia możliwość zawsze odczytu najnowszej wersji danych z usługi Cloud Firestore.
Ogólny rezultat to skalowalny i wysoce dostępny system, który zapewnia niewielkie opóźnienia zarówno w przypadku odczytu, jak i zapisu, niezależnie od obciążenia i skali.
Układ danych
Cloud Firestore to baza danych dokumentów bez schematu. Jednak wewnętrznie dane są rozmieszczone głównie w 2 tabelach w warstwie pamięci w postaci bazy danych relacyjnej:
- Tabela Documents: przechowuje dokumenty.
- Tabela Indeksy: w tej tabeli przechowywane są wpisy indeksu, które umożliwiają wydajne uzyskiwanie wyników i ich sortowanie według wartości indeksu.
Na schemacie poniżej widać, jak poszczególne tabele bazy danych Cloud Firestore mogą wyglądać po podziale. Podziały są replikowane w 3 różnych strefach, a każdy z nich ma przypisanego lidera Paxos.
Jeden region a wiele regionów
Podczas tworzenia bazy danych musisz wybrać region lub wiele regionów.
Pojedyncza lokalizacja regionalna to konkretna lokalizacja geograficzna, np. us-west1. Jak objaś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ę z określonego zestawu regionów, w których są przechowywane 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ę świadka, która nie przechowuje pełnego zestawu danych, ale uczestniczy w replikacji. Dzięki replikowaniu danych między wieloma regionami można je zapisywać i czytać nawet po utracie całego regionu.
Więcej informacji o lokalizacjach w regionie znajdziesz w artykule Cloud Firestore.
Zrozumieć życie w Cloud Firestore
Klient Cloud Firestore może zapisywać dane, tworząc, aktualizując lub usuwając pojedynczy dokument. Zapisywanie danych w pojedynczym dokumencie wymaga zaktualizowania zarówno dokumentu, jak i powiązanych z nim wpisów indeksu w warstwie pamięci. Cloud Firestore obsługuje też operacje atomowe, które składają się z wielu odczytów lub zapisów w co najmniej jednym dokumencie.
W przypadku wszystkich rodzajów zapisów usługa Cloud Firestore zapewnia właściwości ACID (atomowość, spójność, izolacja i trwałość) baz danych relacyjnych. Cloud Firestore zapewnia też możliwość sekwencyjnego przetwarzania, co oznacza, że wszystkie transakcje wyglądają tak, jakby były wykonywane w kolejności.
Najważniejsze kroki w 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. Transakcja umożliwia Cloud Firestore udostępnianie wymienionych 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 również wprowadzenie niezbędnych zmian w tabeli Indexes w ten sposób:
- Pola, które są dodawane do dokumentów, wymagają odpowiednich wstawień w tabeli indeksów.
- Pola, które są usuwane z dokumentów, muszą być odpowiednio usunięte z tabeli Indexes.
- Pola, które są modyfikowane w dokumentach, wymagają zarówno usunięcia (starych wartości), jak i wstawienia (nowych wartości) w tabeli indeksów.
Aby obliczyć wspomniane wcześniej mutacje, Cloud Firestore odczytuje konfigurację indeksowania 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 zbiera je w ramach transakcji, a następnie zatwierdza.
Transakcja zapisu w warstwie pamięci
Jak już wspomnieliśmy, zapis w Cloud Firestore wymaga transakcji odczytu-zapisu w warstwie składowania danych. W zależności od układu danych zapis może obejmować co najmniej 1 podział, jak widać na układzie danych.
Na poniższym diagramie baza danych Cloud Firestore ma 8 podzielonych części (oznaczonych numerami 1–8), które są hostowane na 3 różnych serwerach pamięci masowej w jednej strefie, a każda z nich jest replikowana w 3 lub większej liczbie stref. 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, która zawiera zbiór Restaurants
o tej strukturze:
Klient Cloud Firestore żąda poniższej zmiany dokumentu w kolekcji Restaurant
, aktualizując wartość pola priceCategory
.
Poniżej opisano ogólnie, co się dzieje podczas zapisu:
- Utwórz transakcję zapisu i odczytu.
- Odczytaj dokument
restaurant1
w zbiorzeRestaurants
z tabeli Documents (Dokumenty) na poziomie warstwy pamięci masowej. - Odczytaj indeksy dokumentu z tabeli Indeksy.
- Oblicza mutacje, które zostaną wprowadzone w danych. W tym przypadku występuje 5 mutacji:
- M1: zaktualizuj wiersz
restaurant1
w tabeli Dokumenty, aby odzwierciedlić zmianę wartości polapriceCategory
. - 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 w tabeli Indeksy wiersze dla nowej wartości
priceCategory
w przypadku indeksów malejących i rosnących.
- M1: zaktualizuj wiersz
- Zrealizuj te mutacje.
Klient pamięci masowej w usłudze Cloud Firestore wyszukuje podziały, do których należą klucze wierszy do zmiany. Rozważmy przypadek, w którym grupa 3 wyświetla grupę M1, a grupa 6 – grupy 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.
Poniżej opisujemy, co dzieje się podczas zatwierdzania:
- Klient pamięci masowej wysyła polecenie commit. Commit zawiera mutacje M1–M5.
- Transakcje 3 i 6 są uczestnikami tej transakcji. Jeden z uczestników jest wybierany jako koordynator, np. Grupa 3. Zadaniem koordynatora jest zapewnienie, aby transakcja została zaakceptowana lub przerwana w sposób atomowy dla wszystkich uczestników.
- Repliki liderów tych podziałów odpowiadają za pracę wykonaną przez uczestników i koordynatorów.
- Każdy uczestnik i koordynator uruchamia algorytm Paxos na swoich replikach.
- Lider wykonuje algorytm Paxos z replikami. Kwarum jest osiągane, gdy większość replik odpowiada liderowi odpowiedzią
ok to commit
. - Następnie każdy uczestnik informuje koordynatora, że jest gotowy (pierwszy etap zatwierdzania dwufazowego). Jeśli żaden z uczestników nie może zrealizować transakcji, cała transakcja jest
aborts
.
- Lider wykonuje algorytm Paxos z replikami. Kwarum jest osiągane, gdy większość replik odpowiada liderowi odpowiedzią
- Gdy koordynator wie, że wszyscy uczestnicy, w tym on sam, są gotowi, przekazuje wynik transakcji
accept
wszystkim uczestnikom (drugi etap dwufazowego zatwierdzenia). W tej fazie każdy uczestnik zapisuje decyzję o zatwierdzeniu w stabilnym miejscu przechowywania, a transakcja zostaje zatwierdzona. - Koordynator odpowiada klientowi usługi przechowywania w Cloud Firestore, że transakcja została zatwierdzona. Równocześnie koordynator i wszyscy uczestnicy wprowadzają zmiany w danych.
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 obejmującego wiele regionów rozmieszczenie 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. Dlatego opóźnienie podstawowe w przypadku operacji Cloud Firestore jest nieco większe w porównaniu z wdrożeniami w jednym regionie.
Repliki są konfigurowane w taki sposób, aby prowadzenie w przypadku podziałów zawsze pozostawało w regionie podstawowym. Główny region to region, z którego na serwer Cloud Firestore trafia ruch. 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żda operacja zapisu w komponencie Cloud Firestore wiąże się też z pewną interakcją z silnikiem w czasie rzeczywistym w komponencie 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
Ta sekcja zawiera szczegółowe informacje o samodzielnych odczytach w usłudze Cloud Firestore, które nie są realizowane w czasie rzeczywistym. Wewnętrznie serwer Cloud Firestore obsługuje większość tych zapytań w 2 głównych etapach:
- Skanowanie jednego zakresu w tabeli Indeksy
- punktowe wyszukiwania w tabeli Documents (Dokumenty) na podstawie wyniku wcześniejszego skanowania;
Odczyty danych z poziomu warstwy pamięci są wykonywane wewnętrznie za pomocą transakcji bazy danych, aby zapewnić spójne odczyty. Jednak w przeciwieństwie do transakcji używanych do zapisu te transakcje nie blokują dostępu. Zamiast tego wybierają sygnaturę czasową, a następnie wykonują wszystkie odczyty w tym momencie. Ponieważ nie blokują zasobów, nie blokują też równoczesnych transakcji odczytu-zapisu. Aby wykonać tę transakcję, klient miejsca na dane w Cloud Firestore określa ograniczenie sygnatury czasowej, które informuje warstwę miejsca na dane, 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.
Transakcja odczytu w warstwie pamięci
W tej sekcji opisano typy odczytów i sposób ich przetwarzania na warstwie pamięci w Cloud Firestore.
Mocne lektury
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 do momentu rozpoczęcia odczytu.
Single Split read
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 skrócić czas oczekiwania.
W tym momencie w zależności od wybranej repliki mogą wystąpić te przypadki:
- Żądanie odczytu jest wysyłane do głównej repliki (strefa A).
- Ponieważ lider jest zawsze na bieżąco, odczyt może być przeprowadzony bezpośrednio.
- Żądanie odczytu jest wysyłane do repliki niebędącej liderem (np. strefy B).
- Rozgałęzienie 3 może na podstawie swojego stanu wewnętrznego wiedzieć, że ma wystarczającą ilość informacji do obsłużenia odczytu, i to robi.
- Rozdział 3 nie jest pewien, 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 zastosowaniu tej transakcji można kontynuować odczyt.
Cloud Firestore zwraca odpowiedź klientowi.
Odczyt wielokrotny
W sytuacji, gdy odczyty muszą być wykonywane z wielu podziałów, ten sam mechanizm działa we wszystkich podziale. Gdy zwrócone zostaną dane ze wszystkich podziałów, klient usługi przechowywania danych w Cloud Firestore połączy wyniki. Cloud Firestore przekazuje te dane klientowi.
Nieaktualne odczyty
Silne odczyty są domyślnym trybem 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ć najnowszej wersji danych, a jej funkcje działają dobrze z danymi, które mogą być nieaktualne o kilka sekund.
W takim przypadku klient może zdecydować się na otrzymywanie nieaktualnych odczytów, używając opcji odczytu read_time
. W takim przypadku odczyty są wykonywane tak, jak dane były w czasie read_time
, a najbliższa replika prawdopodobnie już zweryfikowała, że ma dane w określonym czasie read_time
.
Aby uzyskać zauważalnie lepszą wydajność, ustaw wartość 15 sekund. Nawet w przypadku nieaktualnych odczytów wiersze są ze sobą spójne.
Unikaj obszarów o wysokiej aktywności
Podzbiory w Cloud Firestore są automatycznie dzielone na mniejsze części, aby w razie potrzeby lub gdy przestrzeń klucza się powiększy, rozłożyć zadanie obsługi ruchu na więcej serwerów pamięci masowej. Podziały utworzone na potrzeby obsługi nadmiaru ruchu są zachowywane przez około 24 godziny, nawet jeśli ruch się skończy. Jeśli więc występują powtarzające się wzrosty ruchu, podziały są utrzymywane i w razie potrzeby wprowadzane są kolejne podziały. Te mechanizmy ułatwiają bazom danych Cloud Firestore autoskalowanie przy rosnącym obciążeniu lub wielkości bazy danych. Pamiętaj jednak, że istnieją pewne ograniczenia, o których przeczytasz poniżej.
Podział pamięci i załadowania zajmuje czas, a zbyt szybkie zwiększanie natężenia ruchu może spowodować duże opóźnienia lub błędy przekroczenia terminu, które są powszechnie nazywane problemami, gdy usługa się dostosowuje. Sprawdzoną metodą jest rozdzielenie 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 nazywa się regułą 500/50/5 i polega na optymalnym dostosowaniu skali bazy danych do Twojego zbioru zadań.
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 odczytać lub zapisać ten sam dokument w tym samym czasie.
Innym szczególnym przypadkiem hotspottingu jest sytuacja, gdy w funcji Cloud Firestore jako identyfikatora dokumentu używa się klucza rosnącego lub malejącego i występuje znaczna liczba operacji 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ą.
Pamiętaj, że dzięki stosowaniu opisanych wyżej metod usługa Cloud Firestore może się skalować, aby obsługiwać dowolnie duże obciążenia bez konieczności dostosowywania 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
- Dowiedz się więcej o sprawdzonych metodach.
- Dowiedz się więcej o zapytaniach w czasie rzeczywistym na dużą skalę.