Optymalizowanie wydajności zapytań

Aby rozwiązać problemy z powolnymi zapytaniami, użyj funkcji Wyjaśnienie zapytania , aby uzyskać plan wykonania zapytania i profil wykonania w czasie działania. W sekcji poniżej opisujemy, jakie kroki możesz podjąć, aby zoptymalizować wydajność zapytania w zależności od profilu wykonania:

Ogranicz liczbę wyników

Aby sprawdzić, czy zapytanie zwraca wiele dokumentów, użyj pola records returned (zwrócone rekordy) w drzewie wykonania. Rozważ ograniczenie liczby zwracanych dokumentów za pomocą etapu limit(...). Zmniejsza to rozmiar serializowanych bajtów wyników zwracanych klientom przez sieć. Jeśli przed węzłem Limit znajduje się węzeł MajorSort, silnik zapytań może połączyć węzły Limit i MajorSort oraz zastąpić pełną materializację i sortowanie w pamięci sortowaniem TopN, co zmniejsza wymagania dotyczące pamięci dla zapytania.

Ogranicz rozmiar dokumentu wynikowego

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

Używaj indeksów

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

Sprawdź, czy zapytanie używa indeksu

Aby sprawdzić, czy zapytanie używa indeksu, sprawdź węzły liści w drzewie wykonania. Jeśli węzeł liścia w drzewie wykonania jest węzłem TableScan, oznacza to, że zapytanie nie używa indeksu i skanuje dokumenty z pamięci głównej. Jeśli używany jest indeks, węzeł liścia w drzewie wykonania będzie zawierać identyfikator indeksu i pola indeksu.

Znajdź lepszy indeks

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

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

Jeśli zapytanie używa indeksu, ale silnik zapytań nadal wykonuje ponowne sortowanie zestawu wyników w pamięci (co można stwierdzić na podstawie węzła MajorSort w drzewie wykonania zapytania), oznacza to, że używany indeks nie może spełnić wymagań zapytania dotyczących sortowania. Aby utworzyć bardziej odpowiedni indeks, zapoznaj się z następną sekcją.

Tworzenie indeksów

Aby utworzyć indeksy, postępuj zgodnie z dokumentacją dotyczącą zarządzania indeksami . Aby mieć pewność, że zapytanie może używać indeksów, utwórz indeksy zwykłe (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 zapytaniach, uporządkuj pola w kolejności malejącej liczby wystąpień pól w operatorach równości w zapytaniach.
  2. Wszystkie pola, które będą sortowane (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 ograniczenia zapytania.
  4. Pola, które będą zwracane w ramach zapytania w indeksie: uwzględnienie takich pól w indeksie umożliwia pokrycie zapytania przez indeks i uniknięcie konieczności pobierania dokumentu z pamięci głównej.

Wymuś skanowanie indeksu lub tabeli

Gdy wysyłasz zapytanie do Cloud Firestore, usługa automatycznie używa indeksów, które mogą zwiększyć wydajność zapytania. Dzięki temu nie musisz określać indeksu dla zapytań. W przypadku zapytań, które są kluczowe dla Twojego obciążenia, zalecamy jednak używanie opcji forceIndex, aby zapewnić bardziej spójną wydajność.

W niektórych przypadkach Cloud Firestore może wybrać indeks, który powoduje zwiększenie opóźnienia zapytania. Jeśli wykonasz czynności rozwiązywania problemów z regresją wydajności i potwierdzisz, że warto spróbować innego indeksu dla zapytania, możesz określić indeks za pomocą opcji forceIndex.

Opcji forceIndex możesz użyć na dowolnym etapie wejściowym w operacjach Pipeline, aby zastąpić Cloud Firestore's domyślny plan zapytania i określić indeks do użycia lub wymusić skanowanie tabeli.

Wymuś użycie określonego indeksu

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

Poniższy przykład wymusza użycie przez planistę 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, w których warto wymusić użycie określonego indeksu:

  • Testowanie wydajności różnych indeksów.
  • Upewnianie się, że w przypadku zapytania używany jest określony, znany 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 zakończy się niepowodzeniem.

Wymuś skanowanie tabeli

Skanowanie tabeli odczytuje dokumenty w kolekcji lub grupie kolekcji bez użycia indeksów dodatkowych. Aby wymusić skanowanie tabeli, ustaw 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żywaj forceIndex z funkcją Wyjaśnienie zapytania

Aby obserwować efekty działania forceIndex, możesz użyć funkcji Wyjaśnienie zapytania w trybach explain lub analyze:

  • Sprawdź, czy Cloud Firestore używa 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ść zapytania.

Sprawdzone metody używania forceIndex

Chociaż forceIndex zapewnia większą kontrolę nad wykonywaniem zapytań, Cloud Firestore's optymalizator zapytań jest na ogół wydajny w większości przypadków użycia. Podczas korzystania z forceIndex stosuj te sprawdzone metody:

  • Używaj forceIndex z rozwagą. Jeśli zauważysz nieoptymalną wydajność w przypadku domyślnego planu zapytania, użyj funkcji Wyjaśnienie zapytania, aby zdiagnozować problem zanim wymusisz użycie indeksu.
  • Gdy używasz forceIndex, pamiętaj, aby testować zapytania z realistycznymi ilościami danych, aby poznać ich wydajność i koszty.
  • Unikaj używania forceIndex: "primary" w dużych kolekcjach w środowiskach produkcyjnych.

Więcej informacji o wynikach zapytania wykonanego za pomocą funkcji Wyjaśnienie zapytania znajdziesz w sekcji Informacje o wykonywaniu zapytań.