Optymalizuj zapytania za pomocą filtrów zakresów i nierówności w wielu polach

Ta strona zawiera przykłady strategii indeksowania, która powinna być stosowana w przypadku zapytań z filtrami zakresu i nierówności w wielu polach, aby zapewnić jak największą wydajność zapytań.

Zanim zaczniesz optymalizować zapytania, przeczytaj powiązane pojęcia.

Optymalizowanie zapytań za pomocą narzędzia Query Explain

Aby określić, czy użyte zapytania i indeksy są optymalne, możesz użyć narzędzia Query Explain, aby uzyskać podsumowanie planu zapytania i statystyki wykonania zapytania:

Java

Query q = db.collection("employees").whereGreaterThan("salary",
100000).whereGreaterThan("experience", 0);

ExplainResults<QuerySnapshot> explainResults = q.explain(ExplainOptions.builder().analyze(true).build()).get();
ExplainMetrics metrics = explainResults.getMetrics();

PlanSummary planSummary = metrics.getPlanSummary();
ExecutionStats executionStats = metrics.getExecutionStats();

System.out.println(planSummary.getIndexesUsed());
System.out.println(stats.getResultsReturned());
System.out.println(stats.getExecutionDuration());
System.out.println(stats.getReadOperations());
System.out.println(stats.getDebugStats());

Node.js

let q = db.collection("employees")
      .where("salary", ">", 100000)
      .where("experience", ">",0);

let options = { analyze : 'true' };
let explainResults = await q.explain(options);

let planSummary = explainResults.metrics.planSummary;
let stats = explainResults.metrics.executionStats;

console.log(planSummary);
console.log(stats);

Poniższy przykład pokazuje, jak zastosowanie prawidłowej kolejności indeksów zmniejsza liczbę wpisów indeksu skanowanych przez Cloud Firestore.

Proste zapytania

W wcześniejszym przykładzie zbioru pracowników proste zapytanie uruchamiane z indeksem (experience ASC, salary ASC) wygląda tak:

Java

db.collection("employees")
  .whereGreaterThan("salary", 100000)
  .whereGreaterThan("experience", 0)
  .orderBy("experience")
  .orderBy("salary");

Zapytanie skanuje tylko 95 000 wpisów indeksu, aby zwrócić 5 dokumentów. Ponieważ predykat zapytania nie jest spełniony, odczytywana jest duża liczba wpisów indeksu, ale są one odfiltrowywane.

// Output query planning info
{
    "indexesUsed": [
        {
            "properties": "(experience ASC, salary ASC, __name__ ASC)",
            "query_scope": "Collection"
        }
    ],

    // Output Query Execution Stats
    "resultsReturned": "5",
    "executionDuration": "2.5s",
    "readOperations": "100",
    "debugStats": {
        "index_entries_scanned": "95000",
        "documents_scanned": "5",
        "billing_details": {
            "documents_billable": "5",
            "index_entries_billable": "95000",
            "small_ops": "0",
            "min_query_cost": "0"
        }
    }
}

Na podstawie specjalistycznej wiedzy w danej dziedzinie możemy wywnioskować, że większość pracowników ma co najmniej pewne doświadczenie, ale niewiele osób zaoferuje wynagrodzenie powyżej 1 000 000. Na podstawie tych statystyk możemy stwierdzić, że ograniczenie salary jest bardziej selektywne niż ograniczenie experience. Aby wpłynąć na indeks używany przez Cloud Firestore do wykonania zapytania, przed ograniczeniem experience określ klauzulę orderBy, która porządkuje ograniczenie salary.

Java

db.collection("employees")
  .whereGreaterThan("salary", 100000)
  .whereGreaterThan("experience", 0)
  .orderBy("salary")
  .orderBy("experience");

Jeśli dodasz predykaty za pomocą klauzuli orderBy(), Cloud Firestore uruchomi zapytanie za pomocą indeksu (salary ASC, experience ASC). W związku z tym selektywność pierwszego filtra zakresu jest w tym zapytaniu większa w porównaniu z wcześniejszym zapytaniem, więc zapytanie działa szybciej i jest bardziej ekonomiczne.

// Output query planning info
{
    "indexesUsed": [
        {
            "properties": "(salary ASC, experience ASC, __name__ ASC)",
            "query_scope": "Collection"
        }
    ],

    // Output Query Execution Stats
    "resultsReturned": "5",
    "executionDuration": "0.2s",
    "readOperations": "6",
    "debugStats": {
        "index_entries_scanned": "1000",
        "documents_scanned": "5",
        "billing_details": {
            "documents_billable": "5",
            "index_entries_billable": "1000",
            "small_ops": "0",
            "min_query_cost": "0"
        }
    }
}

Co dalej