Optymalizowanie wydajności zapytań

Aby rozwiązać problemy z powolnymi zapytaniami, użyj funkcji Query Explain, aby uzyskać plan wykonania zapytania i profil wykonania w czasie działania. W sekcji poniżej znajdziesz opis czynności, które możesz wykonać, aby zoptymalizować wydajność zapytania w zależności od profilu wykonania:

Ograniczanie liczby wyników

Użyj pola zwróconych rekordów w drzewie wykonania, aby sprawdzić, czy zapytanie zwraca wiele dokumentów. Rozważ ograniczenie liczby zwracanych dokumentów za pomocą etapu limit(...). Zmniejsza to rozmiar serializowanych bajtów wyników zwracanych klientom przez sieć. W przypadku, gdy węzeł Limit jest poprzedzony węzłem MajorSort, silnik zapytań może połączyć węzły LimitMajorSort oraz zastąpić pełną materializację i sortowanie w pamięci sortowaniem TopN, co zmniejsza wymagania dotyczące pamięci w przypadku zapytania.

Ograniczanie rozmiaru dokumentu wynikowego

Rozważ ograniczenie rozmiaru zwracanego dokumentu za pomocą parametru select(...), aby zwracać tylko wymagane pola, lub parametru remove_fields(...), aby odrzucać zbyt duże pola. Pomaga to zmniejszyć koszty obliczeniowe i koszty pamięci związane z przetwarzaniem wyników pośrednich oraz rozmiar serializowanych wyników w bajtach, gdy są one zwracane do klientów przez sieć. W przypadku, gdy wszystkie pola, do których odwołuje się zapytanie, są objęte zwykłym indeksem, umożliwia to również pełne pokrycie zapytania przez skanowanie indeksu, co pozwala uniknąć pobierania dokumentów z pamięci podstawowej.

Korzystanie z indeksów

Aby skonfigurować i zoptymalizować indeksy, postępuj zgodnie z tymi instrukcjami.

Sprawdzanie, czy zapytanie używa indeksu

Aby sprawdzić, czy zapytanie korzysta z indeksu, możesz przejrzeć węzły liści w drzewie wykonania. Jeśli węzeł liścia drzewa wykonania jest węzłem TableScan, oznacza to, że zapytanie nie używa indeksu i skanuje dokumenty z pamięci podstawowej. Jeśli używany jest indeks, węzeł liścia drzewa wykonania będzie wyświetlać identyfikator indeksu i pola indeksu.

Znajdowanie lepszego indeksu

Indeks jest przydatny w przypadku zapytania, jeśli może zmniejszyć liczbę dokumentów, które silnik zapytań musi pobrać z pamięci podstawowej, lub jeśli kolejność pól może spełnić wymagania zapytania dotyczące sortowania.

Jeśli zapytanie korzysta z indeksu, ale silnik zapytań nadal pobiera i odrzuca wiele dokumentów (co można stwierdzić na podstawie węzła skanowania, który zwraca wiele rekordów, a następnie węzła filtra, który zwraca niewiele rekordów), oznacza to, że predykat zapytania spełniony przy użyciu indeksu nie jest selektywny. Aby utworzyć bardziej odpowiedni indeks, zapoznaj się z artykułem Tworzenie indeksów.

Jeśli zapytanie korzysta z indeksu, ale mechanizm zapytań nadal wykonuje w pamięci ponowne porządkowanie zbioru wyników, co jest wskazywane przez węzeł MajorSort w drzewie wykonania zapytania, oznacza to, że użyty indeks nie może być używany do spełnienia wymagania sortowania zapytania. Aby utworzyć bardziej odpowiedni indeks, zapoznaj się z następną sekcją.

Tworzenie indeksów

Postępuj zgodnie z dokumentacją dotyczącą zarządzania indeksami, aby tworzyć indeksy. Aby mieć pewność, że zapytanie może korzystać z indeksów, utwórz zwykłe indeksy (nie Multikey) z polami w tej kolejności:

  1. Wszystkie pola, które będą używane w operatorach równości. Aby zmaksymalizować szansę ponownego użycia w różnych zapytaniach, uporządkuj pola w kolejności malejącej liczby wystąpień pól w operatorach równości w zapytaniach.
  2. Wszystkie pola, według których będzie sortowana tabela (w tej samej kolejności).
  3. Pola, które będą używane w operatorach zakresu lub nierówności w kolejności malejącej selektywności ograniczeń zapytania.
  4. Pola, które będą zwracane w ramach zapytania w indeksie: uwzględnienie takich pól w indeksie umożliwia mu obsługę zapytania i uniknięcie pobierania dokumentu z pamięci podstawowej.

Wymuszanie skanowania indeksu lub tabeli

Gdy wysyłasz zapytanie Cloud Firestore, automatycznie używa ono indeksów, które prawdopodobnie zwiększą jego wydajność. Dzięki temu nie musisz określać indeksu w zapytaniach. W przypadku zapytań, które mają kluczowe znaczenie dla Twojego obciążenia, zalecamy jednak używanie opcji forceIndex, aby uzyskać bardziej spójną wydajność.

W niektórych przypadkach usługa Cloud Firestore może wybrać indeks, który spowoduje wzrost opóźnienia zapytania. Jeśli po wykonaniu czynności związanych z rozwiązywaniem problemów z regresją wydajności stwierdzisz, że warto wypróbować inny indeks dla zapytania, możesz go określić za pomocą opcji forceIndex.

W przypadku dowolnego etapu wprowadzania danych w operacjach potoku możesz użyć opcji forceIndex, aby zastąpić domyślny plan zapytania Cloud Firestore i określić indeks do użycia lub wymusić skanowanie tabeli.

Wymuszanie konkretnego indeksu

Aby wymusić użycie określonego indeksu w zapytaniu, podaj identyfikator indeksu jako ciąg tekstowy w opcji forceIndex. Identyfikator indeksu znajdziesz w konsoli lub w komunikatach o błędach.

W tym przykładzie wymuszamy użycie przez optymalizator indeksu o identyfikatorze CICAgOi36pgK:

Node.js
// Force Planner to use Index ID CICAgOi36pgK
await db.pipeline()
  .collectionGroup({ collectionId: "customers", forceIndex: "CICAgOi36pgK" })
  .limit(100)
  .execute();
Java
// Force Planner to use Index ID CICAgOi36pgK
Pipeline.Snapshot results1 =
    firestore.pipeline()
      .collectionGroup("customers", new CollectionGroupOptions()
          .withHints(new CollectionHints().withForceIndex("CICAgOi36pgK")))
      .limit(100)
      .execute().get();
Go
// Force Planner to use Index ID CICAgOi36pgK
snapshot1 := client.Pipeline().
	CollectionGroup("customers", firestore.WithForceIndex("CICAgOi36pgK")).
	Limit(100).
	Execute(ctx)

Oto kilka przypadków użycia wymuszania konkretnego indeksu:

  • testowanie skuteczności różnych indeksów,
  • zapewnienie, że w przypadku zapytania używany jest konkretny, optymalny indeks;
  • Zastępowanie optymalizatora, gdy jego domyślny wybór jest nieoptymalny w przypadku konkretnego zapytania.

Jeśli określony indeks nie zostanie znaleziony, zapytanie się nie powiedzie.

Wymuszanie skanowania tabeli

Skanowanie tabeli odczytuje dokumenty w kolekcji lub grupie kolekcji bez używania indeksów dodatkowych. Aby wymusić skanowanie tabeli, ustaw wartość forceIndex na primary.

Poniższy przykład wymusza skanowanie tabeli:

// Force Planner to only do a Full-Table Scan
db.pipeline()
  .collectionGroup({ collectionId: "customers", forceIndex: "primary" })
  .limit(100)

Skanowanie tabeli może być przydatne w tych przypadkach:

  • W przypadku bardzo małych kolekcji, w których narzut indeksu nie jest uzasadniony.
  • W przypadku zapytań, które uzyskują dostęp do większości dokumentów w kolekcji.
  • Do debugowania i porównywania wydajności.

Używanie forceIndex z funkcją Wyjaśnienie zapytania

Za pomocą wyjaśnienia zapytania w trybach explain lub analyze możesz obserwować efekty działania forceIndex:

  • Sprawdź, czy Cloud Firestore użył określonego indeksu w forceIndex, sprawdzając identyfikator indeksu w węzłach liści drzewa wykonania.
  • Sprawdź, czy w planie pojawia się węzeł TableScan, gdy używasz forceIndex: "primary".
  • W trybie analyze porównaj dane o skuteczności, takie jak opóźnienie, liczba skanowanych dokumentów i liczba skanowanych wpisów indeksu, z użyciem i bez użycia forceIndex, aby dostroić wydajność zapytań.

Sprawdzone metody dotyczące forceIndex

forceIndex zapewnia większą kontrolę nad wykonywaniem zapytań, ale optymalizator zapytań w Cloud Firestore jest zwykle skuteczny w większości przypadków użycia. Podczas korzystania z atrybutu forceIndex pamiętaj o tych sprawdzonych metodach:

  • Korzystaj z forceIndex rozważnie. Jeśli zauważysz, że domyślny plan zapytania nie zapewnia optymalnej wydajności, użyj funkcji Query Explain, aby zdiagnozować problem przed wymuszeniem indeksu.
  • Gdy używasz forceIndex, testuj zapytania na realistycznych ilościach danych, aby poznać ich wydajność i charakterystykę kosztów.
  • Unikaj używania forceIndex: "primary" w przypadku dużych kolekcji w środowiskach produkcyjnych.

Więcej informacji o wynikach zapytania wykonanego za pomocą funkcji Wyjaśnij zapytanie znajdziesz w artykule Informacje o wykonaniu zapytania.