Poznawanie wydajności zapytań za pomocą funkcji Query Explain

Query Explain umożliwia przesyłanie zapytań Cloud Firestore do zaplecza i otrzymywanie szczegółowych statystyk wydajności dotyczących wykonywania zapytań na zapleczu. Działa ona podobnie jak operacja EXPLAIN [ANALYZE] w wielu relacyjnych systemach baz danych.

Żądania wyjaśnienia zapytania można wysyłać za pomocą bibliotek klienta serwera Firestore.

Wyniki objaśnień zapytań pomagają zrozumieć, jak są wykonywane zapytania, wskazując nieefektywne rozwiązania i lokalizację prawdopodobnych wąskich gardeł po stronie serwera.

Wyjaśnienie zapytania:

  • Udostępnia statystyki na temat etapu planowania zapytań, dzięki czemu możesz dostosować indeksy zapytań i zwiększyć wydajność.
  • Korzystając z opcji analizy, możesz poznać koszty i wydajność poszczególnych zapytań oraz szybko przeglądać różne wzorce zapytań, aby zoptymalizować ich użycie.

Opisywanie zapytań: domyślna i analiza

Operacje Query Explain można wykonywać za pomocą opcji default (domyślnej) lub analyze (Analiza)

W przypadku opcji domyślnej Query Explain planuje zapytanie, ale pomija etap wykonania. Zwróci ona informacje o etapie planowania. Dzięki temu możesz sprawdzić, czy zapytanie ma niezbędne indeksy, i dowiedzieć się, które indeksy są używane. Dzięki temu możesz na przykład sprawdzić, czy dane zapytanie korzysta z indeksu złożonego zamiast przecięcia wielu różnych indeksów.

W przypadku opcji analizy zapytanie wyjaśnia zarówno plany, jak i wykonuje zapytanie. Zwraca ono wszystkie wspomniane wcześniej informacje z planisty wraz ze statystykami dotyczącymi czasu wykonania zapytania. Zawierać będzie informacje rozliczeniowe dotyczące zapytania oraz statystyki dotyczące jego wykonania na poziomie systemu. Za pomocą tych narzędzi możesz testować różne konfiguracje zapytań i indeksów, aby zoptymalizować ich koszt i opóźnienie.

Ile kosztuje Query Explain?

Gdy używasz opcji domyślnej Query Explain, nie są wykonywane żadne operacje indeksowania ani odczytu. Niezależnie od złożoności zapytania naliczana jest jedna operacja odczytu.

Gdy używasz Query Explain z opcją analizy, wykonywane są operacje indeksowania i odczytu, więc zapytanie jest naliczane w zwykły sposób. Analiza nie wiąże się z dodatkowymi opłatami, tylko z zwykłymi opłatami za wykonywane zapytania.

Korzystanie z Query Explain z opcją domyślną

Aby przesłać żądanie opcji domyślnej, możesz użyć bibliotek klienta.

Pamiętaj, że żądania są uwierzytelniane za pomocą uprawnień z użyciem tych samych uprawnień w przypadku zwykłych operacji dotyczących zapytań. Inne metody uwierzytelniania, takie jak Firebase Authentication, są ignorowane. Więcej informacji znajdziesz w przewodniku Zarządzanie tożsamością w przypadku bibliotek klienta na serwerze.

Java (administrator)

Query q = db.collection("col").whereGreaterThan("a", 1);
ExplainOptions options = ExplainOptions.builder().build();

ExplainResults<QuerySnapshot> explainResults = q.explain(options).get();
ExplainMetrics metrics = explainResults.getMetrics();
PlanSummary planSummary = metrics.getPlanSummary();

    
Węzeł (Administracja)

const q = db.collection('col').where('country', '=', 'USA');
const options = { analyze : 'false' };

const explainResults = await q.explain(options);

const metrics = explainResults.metrics;
const plan = metrics.planSummary;

    

Dokładny format odpowiedzi zależy od środowiska wykonania. Zwrócone wyniki można przekonwertować do formatu JSON. Przykład:

{
    "indexes_used": [
        {"query_scope": "Collection", "properties": "(category ASC, __name__ ASC)"},
        {"query_scope": "Collection", "properties": "(country ASC, __name__ ASC)"},
    ]
}

Więcej informacji znajdziesz w dokumentacji dotyczącej raportu Query Explain.

Używanie narzędzia Query Explain z opcją analizy

Za pomocą bibliotek klienta możesz przesłać prośbę o przeanalizowanie opcji.

Pamiętaj, że żądania są uwierzytelniane za pomocą uprawnień z użyciem tych samych uprawnień w przypadku zwykłych operacji dotyczących zapytań. Inne metody uwierzytelniania, takie jak Firebase Authentication, są ignorowane. Więcej informacji znajdziesz w przewodniku Zarządzanie tożsamością w przypadku bibliotek klienta na serwerze.

Java (Administracja)

Query q = db.collection("col").whereGreaterThan("a", 1);

ExplainOptions options = ExplainOptions.builder().setAnalyze(true).build();

ExplainResults<QuerySnapshot> explainResults = q.explain(options).get();

ExplainMetrics metrics = explainResults.getMetrics();
PlanSummary planSummary = metrics.getPlanSummary();
List<Map<String, Object>> indexesUsed = planSummary.getIndexesUsed();
ExecutionStats stats = metrics.getExecutionStats();

    
Węzeł (Administracja)

const q = db.collection('col').where('country', '=', 'USA');

const options = { analyze : 'true' };

const explainResults = await q.explain(options);

const metrics = explainResults.metrics;
const plan = metrics.planSummary;
const indexesUsed = plan.indexesUsed;
const stats = metrics.executionStats;

    

Poniższy przykład pokazuje, że oprócz obiektu planInfo zwracany jest też obiekt stats. Dokładny format odpowiedzi zależy od środowiska wykonania. Przykładowa odpowiedź jest w formacie JSON.

{
    "resultsReturned": "5",
    "executionDuration": "0.100718s",
    "readOperations": "5",
    "debugStats": {
               "index_entries_scanned": "95000",
               "documents_scanned": "5"
               "billing_details": {
                     "documents_billable": "5",
                     "index_entries_billable": "0",
                     "small_ops": "0",
                     "min_query_cost": "0",
               }
    }

}

Więcej informacji znajdziesz w dokumentacji raportu Wyjaśnienie zapytania.

Interpretowanie wyników i wprowadzanie zmian

Przyjrzyjmy się przykładowemu scenariuszowi, w którym wysyłamy zapytanie o filmy według gatunku i kraju produkcji.

Dla przykładu przyjmij zapytanie SQL o takiej treści.

SELECT *
FROM /movies
WHERE category = 'Romantic' AND country = 'USA';

Jeśli użyjemy opcji analizy, zwrócone dane wskazują, że zapytanie jest wykonywane na 2 indek sach pojedynczego pola: (category ASC, __name__ ASC)(country ASC, __name__ ASC). Skanuje 16 500 wpisów w indeksie, ale zwraca tylko 1200 dokumentów.

// Output query planning info
{
    "indexes_used": [
        {"query_scope": "Collection", "properties": "(category ASC, __name__ ASC)"},
        {"query_scope": "Collection", "properties": "(country ASC, __name__ ASC)"},
    ]
}

// Output query status
{
    "resultsReturned": "1200",
    "executionDuration": "0.118882s",
    "readOperations": "1200",
    "debugStats": {
               "index_entries_scanned": "16500",
               "documents_scanned": "1200"
               "billing_details": {
                     "documents_billable": "1200",
                     "index_entries_billable": "0",
                     "small_ops": "0",
                     "min_query_cost": "0",
               }
    }
}

Aby zoptymalizować wydajność wykonywania zapytania, możesz utworzyć indeks złożony w pełni pokryty (category ASC, country ASC, __name__ ASC).

Po ponownym uruchomieniu zapytania z opcją analizy widzimy, że wybrany został nowo utworzony indeks, a zapytanie jest wykonywane znacznie szybciej i efektywniej.

// Output query planning info
{
    "indexes_used": [
        {"query_scope": "Collection", "properties": "(category ASC, country ASC,  __name__ ASC)"}
    ]
}

// Output query stats
{
    "resultsReturned": "1200",
    "executionDuration": "0.026139s",
    "readOperations": "1200",
    "debugStats": {
               "index_entries_scanned": "1200",
               "documents_scanned": "1200"
               "billing_details": {
                     "documents_billable": "1200",
                     "index_entries_billable": "0",
                     "small_ops": "0",
                     "min_query_cost": "0",
               }
    }
}