Menggunakan Query Explain

Dengan Query Explain, Anda dapat mengirimkan kueri mode Datastore ke backend dan menerima statistik performa mendetail tentang eksekusi kueri backend sebagai gantinya. Library ini berfungsi seperti operasi EXPLAIN ANALYZE di banyak sistem database relasional.

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

Hasil Kueri Explain membantu Anda memahami bagaimana kueri dijalankan, yang menunjukkan inefisiensi dan lokasi kemungkinan bottleneck sisi server.

Penjelasan Kueri:

  • 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 melakukan iterasi dengan cepat melalui berbagai pola kueri untuk mengoptimalkan penggunaannya.

Memahami opsi Penjelasan Kueri: default dan analisis

Operasi Query Explain dapat dilakukan menggunakan opsi default atau opsi analyze.

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

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

Apa yang dimaksud dengan biaya Penjelasan Kueri?

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

Saat kueri dijelaskan dengan opsi analisis, operasi indeks dan baca akan dilakukan, 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 untuk operasi kueri biasa.

Java

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

Untuk melakukan autentikasi ke mode Datastore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, baca 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 paket kueri:

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

Untuk mengetahui informasi selengkapnya tentang laporan, lihat referensi laporan.

Menjalankan kueri dengan opsi analisis

Anda dapat menggunakan library klien untuk mengirimkan permintaan opsi default.

Perlu diperhatikan bahwa hasil analisis kueri diautentikasi dengan Identity and Access Management (IAM), menggunakan izin yang sama untuk 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 API Java mode Datastore.

Untuk melakukan autentikasi ke mode Datastore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, baca 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 mengetahui informasi selengkapnya tentang laporan, 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 mengetahui informasi selengkapnya tentang laporan, lihat referensi laporan Penjelasan Kueri.

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 akan menunjukkan kueri yang dijalankan pada indeks kolom tunggal (category ASC, __name__ ASC) dan (country ASC, __name__ ASC). Metode 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 komposit yang tercakup sepenuhnya (kategori ASC, ASC negara, __name__ ASC).

Dengan menjalankan kembali kueri dalam mode analisis, 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