שימוש ב-Query Explain

התכונה Query Explain מאפשרת לשלוח שאילתות במצב Datastore אל ה-Backend ולקבל בתמורה נתונים סטטיסטיים מפורטים על הביצוע של שאילתות ב-Backend. היא פועלת כמו הפעולה EXPLAIN ANALYZE במערכות רבות של מסדי נתונים רלציוניים.

אפשר לשלוח בקשות להסבר על שאילתות באמצעות ספריות הלקוח של מצב Datastore.

תוצאות של Query Explain עוזרות לכם להבין איך השאילתות מבוצעות, ומציגות לכם חוסר יעילות ומיקום של צווארי בקבוק סבירים בצד השרת.

הסבר לשאילתה:

  • מספק תובנות לגבי שלב התכנון, כדי שתוכלו לשנות את אינדקסים של השאילתות ולשפר את היעילות.
  • הכלי עוזר להבין את העלות והביצועים של כל שאילתה, ומאפשר לחזור במהירות על דפוסי שאילתות שונים כדי לייעל את השימוש בהם.

הסבר על האפשרויות של Query Explain: ברירת מחדל וניתוח

אפשר לבצע פעולות של הסבר על שאילתות באמצעות האפשרות ברירת מחדל או האפשרות ניתוח.

באפשרות ברירת המחדל, Query Explain מתכנן את השאילתה, אבל מדלג על שלב ההפעלה. יוחזרו פרטים על השלב בתהליך התכנון. אפשר להשתמש בכלי הזה כדי לבדוק אם לשאילתה יש את האינדקסים הנדרשים, ולהבין באילו אינדקסים נעשה שימוש. לדוגמה, כך תוכלו לוודא ששאילתה מסוימת משתמשת באינדקס מורכב במקום לבצע חיתוך בין הרבה אינדקסים שונים.

באמצעות האפשרות analyze, ‏ Query Explain מתכנן ומבצע את השאילתה. השאילתה הזו מחזירה את כל המידע על מתכנן השאילתות שצוין קודם, וגם את נתוני הסטטיסטיקה של זמן הריצה של ביצוע השאילתה. המידע הזה יכלול פרטי חיוב וגם תובנות ברמת המערכת לגבי ביצוע השאילתה. אתם יכולים להשתמש בכלים האלה כדי לבדוק הגדרות שונות של שאילתות ואינדקסים, ולבצע אופטימיזציה של העלות והחביון שלהם.

מה העלות של Query Explain?

כשמסבירים שאילתה באמצעות אפשרות ברירת המחדל, לא מתבצעות פעולות אינדקס או קריאה. לא משנה מה מורכבות השאילתה, נגבית עלות של פעולת קריאה אחת.

כשמסבירים שאילתה באמצעות האפשרות analyze, מתבצעות פעולות של אינדוקס וקריאה, ולכן תחויבו על השאילתה כרגיל. אין חיוב נוסף על פעילות הניתוח, רק החיוב הרגיל על השאילתה שמופעלת.

הפעלת שאילתה עם אפשרות ברירת המחדל

אפשר להשתמש בספריית לקוח כדי לשלוח בקשה לאפשרות ברירת מחדל.

שימו לב שתוצאות ההסבר של השאילתה מאומתות באמצעות ניהול זהויות והרשאות גישה, תוך שימוש באותן הרשאות של פעולות שאילתה רגילות.

Java

מידע על התקנת ספריית הלקוח למצב Datastore ושימוש בה מופיע במאמר ספריות הלקוח של מצב Datastore. מידע נוסף מופיע במאמרי העזרה של Datastore mode Java API.

כדי לבצע אימות במצב Datastore, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לסביבת פיתוח מקומית.


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)"},
]

מידע נוסף על הדוח זמין במאמר הסבר על הדוח.

הרצת שאילתה עם האפשרות analyze

אפשר להשתמש בספריית לקוח כדי לשלוח בקשה לאפשרות ברירת מחדל.

שימו לב שתוצאות הניתוח של השאילתה מאומתות באמצעות ניהול זהויות והרשאות גישה (IAM), עם אותן הרשאות של פעולות רגילות של שאילתות.

Java

מידע על התקנת ספריית הלקוח למצב Datastore ושימוש בה מופיע במאמר ספריות הלקוח של מצב Datastore. מידע נוסף מופיע במאמרי העזרה של Datastore mode Java API.

כדי לבצע אימות במצב Datastore, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לסביבת פיתוח מקומית.

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

מידע נוסף על הדוח זמין במאמר הסבר על הדוח.

פירוש התוצאות וביצוע שינויים

בתרחיש לדוגמה הבא, מתבצעת שאילתה על סרטים לפי ז'אנר ומדינה שבה הם הופקו, ומוסבר איך לשפר את האינדקסים שבהם נעשה שימוש בשאילתה.

מידע נוסף על הדוח זמין במאמר מקור המידע על דוח ההסבר של השאילתה.

לדוגמה, נניח שיש שאילתת SQL ששווה ערך לזו.

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

אם נשתמש באפשרות analyze, פלט הדוח הבא יראה שהשאילתה מורצת על אינדקסים של שדה יחיד (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",
               }
    }
}

המאמרים הבאים