Comprendere le prestazioni delle query utilizzando Query Explain

Query Explain ti consente di inviare query Firestore al backend e ricevere statistiche dettagliate sulle prestazioni relative all'esecuzione delle query di backend. Funziona in modo simile all'operazione EXPLAIN [ANALYZE] in molti sistemi di database relazionali.

Le richieste Query Explain possono essere inviate utilizzando le librerie client di Firestore.

I risultati di Query Explain ti aiutano a capire come vengono eseguite le query, mostrando inefficienze e posizione di probabili colli di bottiglia lato server.

Spiegazione della query:

  • Fornisce insight sulla fase di pianificazione delle query in modo da poter regolare gli indici delle query e aumentare l'efficienza.
  • L'opzione di analisi ti consente di comprendere costi e prestazioni per singola query e di eseguire rapidamente l'iterazione di diversi pattern di query al fine di ottimizzarne l'utilizzo.

Comprendere le opzioni di Query Explain: predefinite e analisi

Le operazioni di Query Explain possono essere eseguite utilizzando l'opzione default o l'opzione analyze.

Con l'opzione predefinita, Query Explain pianifica la query, ma salta la fase di esecuzione. Verranno restituite le informazioni sulla fase dello strumento di pianificazione. Puoi utilizzarlo per verificare che una query abbia gli indici necessari e capire quali indici vengono utilizzati. Questo ti aiuterà a verificare, ad esempio, che una determinata query utilizzi un indice composto anziché dover intersecarsi su molti indici diversi.

Con l'opzione di analisi, Query Explain pianifica ed esegue la query. Restituisce tutte le informazioni sullo strumento di pianificazione menzionate in precedenza, insieme alle statistiche relative al runtime di esecuzione della query. Verranno incluse le informazioni di fatturazione della query e gli insight a livello di sistema sull'esecuzione della query. Puoi utilizzare questi strumenti per testare varie configurazioni di query e indici al fine di ottimizzarne costi e latenza.

Quanto costa Query Explain?

Quando utilizzi Query Explain con l'opzione predefinita, non vengono eseguite operazioni di lettura o indice. Indipendentemente dalla complessità della query, viene addebitata un'operazione di lettura.

Quando utilizzi Query Explain con l'opzione di analisi, vengono eseguite le operazioni di indicizzazione e lettura, quindi il costo della query ti viene addebitato come di consueto. Non è previsto nessun costo aggiuntivo per l'attività di analisi, ma solo il normale addebito per la query che viene eseguita.

Utilizza Query Explain con l'opzione predefinita

Puoi utilizzare le librerie client per inviare una richiesta di opzioni predefinita.

Tieni presente che le richieste vengono autenticate con IAM utilizzando le stesse autorizzazioni per le normali operazioni di query. Altre tecniche di autenticazione, come Firebase Authentication, vengono ignorate. Per ulteriori informazioni, consulta la guida su IAM per le librerie client del server.

Java (amministratore)

Query q = db.collection("col").whereGreaterThan("a", 1);
ExplainOptions options = ExplainOptions.builder().build();

ExplainResults<QuerySnapshot> explainResults = q.explain(options).get();
ExplainMetrics metrics = explainResults.getMetrics();
PlanSummary planSummary = metrics.getPlanSummary();

    
Nodo (amministratore)

const q = db.collection('col').where('country', '=', 'USA');
const options = { analyze : 'false' };

const explainResults = await q.explain(options);

const metrics = explainResults.metrics;
const plan = metrics.planSummary;

    

Il formato esatto della risposta dipende dall'ambiente di esecuzione. I risultati restituiti possono essere convertiti in JSON. Ad esempio:

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

Per ulteriori informazioni, consulta la documentazione di riferimento per il report di spiegazione delle query.

Utilizzare Query Explain con l'opzione Analizza

Puoi utilizzare le librerie client per inviare una richiesta di opzione di analisi.

Tieni presente che le richieste vengono autenticate con IAM utilizzando le stesse autorizzazioni per le normali operazioni di query. Altre tecniche di autenticazione, come Firebase Authentication, vengono ignorate. Per ulteriori informazioni, consulta la guida su IAM per le librerie client del server.

Java (amministratore)

Query q = db.collection("col").whereGreaterThan("a", 1);

ExplainOptions options = ExplainOptions.builder().setAnalyze(true).build();

ExplainResults<QuerySnapshot> explainResults = q.explain(options).get();

ExplainMetrics metrics = explainResults.getMetrics();
PlanSummary planSummary = metrics.getPlanSummary();
List<Map<String, Object>> indexesUsed = planSummary.getIndexesUsed();
ExecutionStats stats = metrics.getExecutionStats();

    
Nodo (amministratore)

const q = db.collection('col').where('country', '=', 'USA');

const options = { analyze : 'true' };

const explainResults = await q.explain(options);

const metrics = explainResults.metrics;
const plan = metrics.planSummary;
const indexesUsed = plan.indexesUsed;
const stats = metrics.executionStats;

    

L'esempio seguente mostra l'oggetto stats restituito oltre a planInfo. Il formato esatto della risposta dipende dall'ambiente di esecuzione. La risposta di esempio è in formato JSON.

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

}

Per ulteriori informazioni, consulta la documentazione di riferimento per il report di spiegazione delle query.

Interpretare i risultati e apportare le modifiche

Vediamo uno scenario di esempio in cui eseguiamo query sui film per genere e paese di produzione.

Ad esempio, supponiamo che l'equivalente di questa query SQL.

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

Se utilizziamo l'opzione di analisi, le metriche restituite mostrano che la query viene eseguita su due indici a campo singolo, (category ASC, __name__ ASC) e (country ASC, __name__ ASC). Analizza 16.500 voci di indice, ma restituisce solo 1200 documenti.

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

Per ottimizzare le prestazioni dell'esecuzione della query, puoi creare un indice composto completamente coperto (category ASC, country ASC, __name__ ASC).

Eseguendo nuovamente la query con l'opzione di analisi, possiamo notare che l'indice appena creato è selezionato per questa query e l'esecuzione della query è molto più veloce ed efficiente.

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