Menggunakan Query Explain

Query Explain memungkinkan Anda mengirimkan kueri mode Datastore ke backend dan menerima statistik performa terperinci tentang eksekusi kueri backend sebagai gantinya. Fitur ini berfungsi seperti operasi EXPLAIN ANALYZE di banyak sistem database relasional.

Anda dapat mengirim permintaan Query Explain menggunakan library klien mode Datastore.

Hasil Query Explain membantu Anda memahami cara kueri dijalankan, yang menunjukkan inefisiensi dan lokasi kemungkinan adanya bottleneck sisi server.

Query Explain:

  • Memberikan insight tentang fase perencanaan sehingga Anda dapat menyesuaikan indeks kueri dan meningkatkan efisiensi.
  • Membantu Anda memahami biaya dan performa per kueri dan memungkinkan Anda dengan cepat melakukan iterasi pola kueri yang berbeda guna mengoptimalkan penggunaannya.

Memahami opsi Query Explain: opsi default dan analisis

Operasi Query Explain dapat dijalankan menggunakan opsi default atau opsi analisis.

Dengan opsi default, Query Explain merencanakan kueri, tetapi melewati tahap eksekusi. Opsi ini akan menampilkan informasi tahap perencana. Anda dapat menggunakannya untuk memeriksa apakah kueri memiliki indeks yang diperlukan dan memahami indeks yang digunakan. Hal ini akan membantu Anda memverifikasi, misalnya, bahwa kueri tertentu menggunakan indeks gabungan daripada harus bersinggungan dengan banyak indeks yang berbeda.

Dengan opsi analisis, Query Explain merencanakan sekaligus menjalankan kueri. Opsi ini akan menampilkan semua informasi perencana yang disebutkan sebelumnya beserta statistik dari runtime eksekusi kueri. Hal ini mencakup informasi penagihan beserta insight tingkat sistem ke dalam eksekusi kueri. Anda dapat menggunakan alat ini untuk menguji berbagai konfigurasi kueri dan indeks untuk mengoptimalkan biaya dan latensi.

Berapa biaya Query Explain?

Saat kueri dijelaskan dengan opsi default, tidak ada operasi indeks atau baca yang dijalankan. Terlepas dari kompleksitas kueri, satu operasi baca akan dikenai biaya.

Saat kueri dijelaskan dengan opsi analisis, operasi indeks dan baca akan dijalankan, sehingga Anda akan dikenai biaya untuk kueri tersebut seperti biasa. Tidak ada biaya tambahan untuk aktivitas analisis, hanya biaya biasa untuk kueri yang dijalankan.

Menjalankan kueri dengan opsi default

Anda dapat menggunakan library klien untuk mengirimkan permintaan opsi default.

Perhatikan bahwa hasil penjelasan kueri diautentikasi dengan Identity and Access Management, menggunakan izin yang sama dengan operasi kueri reguler.

Java

Untuk mempelajari cara menginstal dan menggunakan library klien untuk mode Datastore, lihat Library klien mode Datastore. Untuk mengetahui informasi selengkapnya, lihat Dokumentasi referensi Java API mode Datastore.

Untuk melakukan autentikasi ke mode Datastore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.


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)));
  }
}

Lihat kolom indexes_used dalam respons untuk mempelajari indeks yang digunakan dalam rencana kueri:

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

Untuk informasi selengkapnya tentang laporan ini, lihat referensi laporan.

Menjalankan kueri dengan opsi analisis

Anda dapat menggunakan library klien untuk mengirimkan permintaan opsi default.

Perhatikan bahwa hasil analisis kueri diautentikasi dengan Identity and Access Management (IAM), menggunakan izin yang sama dengan operasi kueri reguler.

Java

Untuk mempelajari cara menginstal dan menggunakan library klien untuk mode Datastore, lihat Library klien mode Datastore. Untuk mengetahui informasi selengkapnya, lihat Dokumentasi referensi Java API mode Datastore.

Untuk melakukan autentikasi ke mode Datastore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

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);
    }
  }
}

Lihat objek executionStats untuk menemukan informasi pembuatan profil kueri seperti:

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

Untuk informasi selengkapnya tentang laporan ini, lihat referensi laporan.

Menafsirkan hasil dan melakukan penyesuaian

Contoh skenario berikut membuat kueri film berdasarkan genre dan negara produksi serta menunjukkan cara mengoptimalkan indeks yang digunakan oleh kueri.

Untuk informasi selengkapnya tentang laporan ini, lihat referensi laporan Query Explain.

Sebagai ilustrasi, asumsikan nilai yang setara dengan kueri SQL ini.

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

Jika kita menggunakan opsi analisis, output laporan berikut menunjukkan kueri berjalan pada indeks kolom tunggal (category ASC, __name__ ASC) dan (country ASC, __name__ ASC). Opsi ini memindai 16.500 entri indeks, tetapi hanya menampilkan 1.200 dokumen.

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

Untuk mengoptimalkan performa eksekusi kueri, Anda dapat membuat indeks gabungan yang tercakup sepenuhnya (ASC kategori, ASC negara, ASC __name__).

Dengan menjalankan kueri dalam mode analisis lagi, kita dapat melihat bahwa indeks yang baru dibuat dipilih untuk kueri ini, dan kueri berjalan jauh lebih cepat dan lebih efisien.

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

Langkah selanjutnya