Abfragen mit Bereichs- und Ungleichheitsfiltern für mehrere Attribute optimieren

Auf dieser Seite finden Sie Beispiele für Indexierungsstrategien, die bei Abfragen mit Bereichs- und Ungleichheitsfiltern für mehrere Felder verwendet werden sollten, um eine effiziente Abfrageumgebung zu schaffen.

Informieren Sie sich über Konzepte zu Bereichs- und Ungleichheitsfiltern für mehrere Attribute, bevor Sie Ihre Abfragen optimieren.

Abfragen mit Query Explain optimieren

Um festzustellen, ob die verwendete Abfrage und die verwendeten Indexe optimal sind, können Sie mit Query Explain eine Abfrage erstellen und sich die Ausführungszusammenfassung ansehen.

Java

...
// Build the query
Query<Entity> query =
    Query.newEntityQueryBuilder()
        .setKind("employees")
        .setFilter(
            CompositeFilter.and(
                PropertyFilter.gt("salary", 100000), PropertyFilter.gt("experience", 0)))
        .setOrderBy(OrderBy("experience"), OrderBy("salary"))
        .build();

// Set the explain options to get back *only* the plan summary
ExplainResults<Entity> explainResults = datastore.run(query, ExplainOptions.newBuilder().build());

// Get the explain metrics
Optional<ExplainMetrics> explainMetrics = results.getExplainMetrics();
if (!explainMetrics.isPresent()) {
  throw new Exception("No explain metrics returned");
}

// Get the plan summary
PlanSummary planSummary = explainMetrics.get().getPlanSummary();
List<Map<String, Object>> indexesUsed = planSummary.getIndexesUsed();
System.out.println("----- Indexes Used -----");
indexesUsed.forEach(map -> map.forEach((s, o) -> System.out.println(s + ": " + o)));

// Get the execution stats
if (!explainMetrics.getExecutionStats().isPresent()) {
  throw new Exception("No execution stats returned");
}

ExecutionStats queryStats = explainMetrics.getExecutionStats().get();
Map<String, Object> debugStats = queryStats.getDebugStats();
System.out.println("----- Debug Stats -----");
debugStats.forEach((s, o) -> System.out.println(s + ": " + o));

Das folgende Beispiel zeigt, wie durch die Verwendung der richtigen Indexreihenfolge die Anzahl der Entitäten eingespart wird, die Firestore im Datastore-Modus scannt.

Einfache Abfragen

Im vorherigen Beispiel einer Sammlung von Mitarbeitern lautet die einfache Abfrage, die mit dem Index (salary, experience) ausgeführt wird, so:

GQL

SELECT *
FROM /employees
WHERE salary > 100000 AND experience > 0
ORDER BY experience, salary;

Java

Query<Entity> query =
   Query.newEntityQueryBuilder()
    .setKind("employees")
    .setFilter(
        CompositeFilter.and(
            PropertyFilter.gt("salary", 100000), PropertyFilter.gt("experience", 0)))
    .setOrderBy(OrderBy("experience"), OrderBy("salary"))
    .build();

Die Abfrage scannt 95.000 Indexeinträge und gibt nur 5 Entitäten zurück. Eine große Anzahl von Indexeinträgen wurde gelesen, aber herausgefiltert, da sie das Abfrageprädikat nicht erfüllten.

// Output query planning info
{
        "indexesUsed": [
            {
                "query_scope": "Collection Group",
                "properties": "(experience ASC, salary ASC, __name__ ASC)"
            }
        ]
    },
    // 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"
            }
        }
    }

Wie im vorherigen Beispiel können wir daraus ableiten, dass die Einschränkung salary selektiver ist als die Einschränkung experience.

GQL

SELECT *
FROM /employees
WHERE salary > 100000 AND experience > 0
ORDER BY salary, experience;

Java

Query<Entity> query =
   Query.newEntityQueryBuilder()
    .setKind("employees")
    .setFilter(
        CompositeFilter.and(
            PropertyFilter.gt("salary", 100000), PropertyFilter.gt("experience", 0)))
    .setOrderBy(OrderBy("salary"), OrderBy("experience"))
    .build();

Wenn Sie die Klausel orderBy() explizit verwenden, um die Prädikate in der früheren Reihenfolge hinzuzufügen, verwendet Firestore im Datastore-Modus den Index (salary, experience) zum Ausführen der Abfrage. Da die Auswahl des ersten Bereichsfilters besser als die vorherige Abfrage ist, wird die Abfrage daher schneller und kostengünstiger ausgeführt.

    // Output query planning info
{
    "indexesUsed": [
        {
            "query_scope": "Collection Group",
            "properties": "(salary ASC, experience ASC, __name__ ASC)"
        }
    ],
    // 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"
            }
        }
    }

Weitere Informationen