Query Explain 사용

Query Explain을 사용하면 Datastore 모드 쿼리를 백엔드에 제출하고 그 대가로 백엔드 쿼리 실행에 대한 자세한 성능 통계를 받을 수 있습니다. 이는 여러 관계형 데이터베이스 시스템에서 EXPLAIN ANALYZE 작업과 같은 기능을 합니다.

Datastore 모드 클라이언트 라이브러리를 사용하여 Query Explain 요청을 보낼 수 있습니다.

Query Explain 결과는 쿼리가 실행되는 방식을 이해하는 데 도움이 되므로 비효율성과 서버 측 병목 현상이 발생할 가능성이 있는 위치를 보여줍니다.

Query Explain:

  • 쿼리 색인을 조정하고 효율성을 높일 수 있도록 계획 단계에 대한 유용한 정보를 제공합니다.
  • 쿼리별로 비용 및 성능을 파악하는 데 도움이 되므로 이를 통해 다양한 쿼리 패턴을 빠르게 반복하여 사용량을 최적화할 수 있습니다.

Query Explain 옵션 이해: 기본 및 분석

Query Explain 작업은 기본 옵션 또는 분석 옵션을 사용하여 수행할 수 있습니다.

기본 옵션을 사용하면 Query Explain에서 쿼리를 계획하고 실행 단계를 건너뜁니다. 그러면 플래너 단계 정보가 반환됩니다. 이를 사용하여 쿼리에 필요한 색인이 있는지 확인하고 어떤 색인이 사용되는지 파악할 수 있습니다. 예를 들어 특정 쿼리가 여러 색인을 교차할 필요 없이 복합 색인을 사용하고 있는지 확인하는 데 도움이 됩니다.

분석 옵션을 사용하면 Query Explain에서 쿼리를 계획하고 실행합니다. 그러면 쿼리 실행 런타임의 통계와 함께 앞에서 언급한 모든 플래너 정보가 반환됩니다. 여기에는 쿼리 실행에 대한 시스템 수준의 유용한 정보와 함께 결제 정보가 포함됩니다. 이 도구를 사용하면 다양한 쿼리 및 색인 구성을 테스트하여 비용과 지연 시간을 최적화할 수 있습니다.

Query Explain의 비용

기본 옵션을 사용하여 쿼리를 설명하는 경우 색인 또는 읽기 작업이 수행되지 않습니다. 쿼리 복잡성과 관계없이 읽기 작업 1회에 대한 요금이 청구됩니다.

분석 옵션을 사용하여 쿼리를 설명하는 경우 색인 및 읽기 작업이 수행되므로 쿼리에 대한 요금이 평소와 같이 청구됩니다. 분석 활동에 대한 추가 요금은 없으며 실행 중인 쿼리에 대한 일반적인 요금만 청구됩니다.

기본 옵션을 사용하여 쿼리 실행

클라이언트 라이브러리를 사용하여 기본 옵션 요청을 제출할 수 있습니다.

쿼리 설명 결과는 일반 쿼리 작업과 동일한 권한을 사용하여 Identity and Access Management로 인증됩니다.

Java

Datastore 모드용 클라이언트 라이브러리를 설치하고 사용하는 방법은 Datastore 모드 클라이언트 라이브러리를 참조하세요. 자세한 내용은 Datastore 모드 Java API 참고 문서를 확인하세요.

Datastore 모드에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.


import com.google.cloud.datastore.Datastore;
import com.google.cloud.datastore.DatastoreOptions;
import com.google.cloud.datastore.Entity;
import com.google.cloud.datastore.Query;
import com.google.cloud.datastore.QueryResults;
import com.google.cloud.datastore.models.ExplainMetrics;
import com.google.cloud.datastore.models.ExplainOptions;
import com.google.cloud.datastore.models.PlanSummary;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class QueryProfileExplain {
  public static void invoke() throws Exception {
    // Instantiates a client
    Datastore datastore = DatastoreOptions.getDefaultInstance().getService();

    // Build the query
    Query<Entity> query = Query.newEntityQueryBuilder().setKind("Task").build();

    // Set the explain options to get back *only* the plan summary
    QueryResults<Entity> results = 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");
    }
    PlanSummary planSummary = explainMetrics.get().getPlanSummary();
    List<Map<String, Object>> indexesUsed = planSummary.getIndexesUsed();
    System.out.println("----- Indexes Used -----");
    indexesUsed.forEach(map -> map.forEach((key, val) -> System.out.println(key + ": " + val)));
  }
}

쿼리 계획에 사용되는 색인에 대해 알아보려면 응답의 indexes_used 필드를 참조하세요.

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

보고서에 대한 자세한 내용은 보고서 참조를 확인하세요.

분석 옵션을 사용하여 쿼리 실행

클라이언트 라이브러리를 사용하여 기본 옵션 요청을 제출할 수 있습니다.

쿼리 분석 결과는 일반 쿼리 작업과 동일한 권한을 사용하여 Identity and Access Management로 인증됩니다.

Java

Datastore 모드용 클라이언트 라이브러리를 설치하고 사용하는 방법은 Datastore 모드 클라이언트 라이브러리를 참조하세요. 자세한 내용은 Datastore 모드 Java API 참고 문서를 확인하세요.

Datastore 모드에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.

import com.google.cloud.datastore.Datastore;
import com.google.cloud.datastore.DatastoreOptions;
import com.google.cloud.datastore.Entity;
import com.google.cloud.datastore.Query;
import com.google.cloud.datastore.QueryResults;
import com.google.cloud.datastore.models.ExecutionStats;
import com.google.cloud.datastore.models.ExplainMetrics;
import com.google.cloud.datastore.models.ExplainOptions;
import com.google.cloud.datastore.models.PlanSummary;
import java.util.List;
import java.util.Map;

public class QueryProfileExplainAnalyze {
  public static void invoke() throws Exception {
    // Instantiates a client
    Datastore datastore = DatastoreOptions.getDefaultInstance().getService();

    // Build the query
    Query<Entity> query = Query.newEntityQueryBuilder().setKind("Task").build();

    // Set explain options with analzye = true to get back the query stats, plan info, and query
    // results
    QueryResults<Entity> results =
        datastore.run(query, ExplainOptions.newBuilder().setAnalyze(true).build());

    // Get the result set stats
    if (!results.getExplainMetrics().isPresent()) {
      throw new Exception("No explain metrics returned");
    }
    ExplainMetrics explainMetrics = results.getExplainMetrics().get();

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

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

    long resultsReturned = executionStats.getResultsReturned();
    System.out.println("Results returned: " + resultsReturned);

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

    if (!results.hasNext()) {
      throw new Exception("query yielded no results");
    }

    // Get the query results
    System.out.println("----- Query Results -----");
    while (results.hasNext()) {
      Entity entity = results.next();
      System.out.printf("Entity: %s%n", entity);
    }
  }
}

다음과 같은 쿼리 프로파일링 정보를 찾으려면 executionStats 객체를 참조하세요.

{
    "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",
               }
    }
}

보고서에 대한 자세한 내용은 보고서 참조를 확인하세요.

결과 해석 및 조정

다음 예시 시나리오는 장르 및 제작 국가별로 영화를 쿼리하고 쿼리에 사용되는 색인을 최적화하는 방법을 보여줍니다.

보고서에 대한 자세한 내용은 Query Explain 보고서 참조를 확인하세요.

예를 들어 다음 SQL 쿼리와 동일하다고 가정해 보겠습니다.

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

분석 옵션을 사용하는 경우 다음 보고서 출력은 단일 필드 색인 (category ASC, __name__ ASC)(country ASC, __name__ ASC)에서 실행되는 쿼리를 보여줍니다. 16,500개의 색인 항목을 스캔하지만 1,200개의 문서만 반환합니다.

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

쿼리 실행 성능을 최적화하기 위해 완전히 적용된 복합 색인(category ASC, country ASC, __name__ ASC)을 만들 수 있습니다.

분석 모드에서 쿼리를 다시 실행하면 이 쿼리에 대해 새로 생성된 색인이 선택되고 쿼리가 훨씬 더 빠르고 효율적으로 실행되는 것을 확인할 수 있습니다.

// Output query planning info
    "indexes_used": [
        {"query_scope": "Collection Group", "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",
               }
    }
}

다음 단계