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

Na tej stronie znajdziesz przykłady strategii indeksowania, których możesz używać w zapytaniach z filtrami zakresu i nierówności w różnych polach, aby zwiększyć wydajność zapytań.

Zanim zoptymalizujesz zapytania, zapoznaj się z powiązanymi pojęciami.

Optymalizowanie zapytań za pomocą Query Explain

Aby sprawdzić, czy zapytanie i indeksy są optymalne, możesz użyć polecenia Query Explain, aby uzyskać podsumowanie planu zapytania i statystyki jego wykonania:

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 indeksu zmniejsza liczbę pozycji indeksu, które skanuje Cloud Firestore.

proste zapytania,

W przypadku wcześniejszego przykładu zbioru pracowników proste zapytanie, które jest wykonywane przy użyciu indeksu (experience ASC, salary ASC), ma postać:

Java

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

Zapytanie skanuje 95 tys. pozycji indeksu, aby zwrócić tylko 5 dokumentów. Ponieważ predykat zapytania nie jest spełniony, odczytywana jest duża liczba wpisów w indeksie, 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"
        }
    }
}

Z tego, że jest to praca specjalistyczna, możesz wnioskować, że większość pracowników będzie miała co najmniej pewne doświadczenie, ale niewielu z nich będzie zarabiać ponad 100 tys. zł. Z tego wykresu wynika, że ograniczenie salary jest bardziej selektywne niż ograniczenie experience. Aby wpływać na indeks, którego Cloud Firestore używa do wykonywania zapytania, określ klauzulę orderBy, która porządkuje ograniczenie salary przed ograniczeniem experience.

Java

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

Gdy dodajesz predykaty, używając wyraźnie klauzuli orderBy(), funkcja Cloud Firestore używa indeksu (salary ASC, experience ASC) do wykonywania zapytań. W tym zapytaniu pierwsze ograniczenie zakresu jest bardziej selektywne niż w poprzednim zapytaniu, więc jest ono wykonywane szybciej i bardziej ekonomicznie.

// 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?