여러 속성에서 범위 및 불일치 필터를 사용하여 쿼리 최적화

이 페이지에서는 효율적인 쿼리 환경을 만들기 위해 여러 필드에 범위 및 불일치 필터가 있는 쿼리에 사용할 수 있는 색인 생성 전략의 예시를 제공합니다.

쿼리를 최적화하기 전에 여러 속성의 범위 및 불일치 필터 개념을 읽어보세요.

Query Explain으로 쿼리 최적화

사용된 쿼리와 색인이 최적의 상태인지 확인하기 위해 Query Explain을 사용하여 쿼리를 만들고 실행 요약을 검토할 수 있습니다.

자바

...
// 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));

다음 예시는 올바른 색인 순서를 사용하여 Datastore 모드의 Firestore에서 스캔하는 항목 수를 줄이는 방법을 보여줍니다.

간단한 쿼리

직원 컬렉션의 이전 예시에서 (salary, experience) 색인을 사용해 실행되는 간단한 쿼리는 다음과 같습니다.

GQL

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

자바

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

이 쿼리는 95,000개의 색인 항목을 스캔하여 5개 항목만을 반환합니다. 많은 수의 색인 항목을 읽었지만 쿼리 조건자를 충족하지 않아 필터링되었습니다.

// 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"
            }
        }
    }

앞의 예시에 따라 salary 제약조건이 experience 제약조건보다 선택성이 높다고 추론할 수 있습니다.

GQL

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

자바

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

orderBy() 절을 명시적으로 사용하여 앞의 순서로 조건자를 추가하면 Datastore 모드의 Firestore는 (salary, experience) 색인을 사용하여 쿼리를 실행합니다. 첫 번째 범위 필터의 선택이 이전 쿼리보다 우수하므로 쿼리가 더 빠르게 실행되며 비용 효율적입니다.

    // 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"
            }
        }
    }

다음 단계