Entender o desempenho da consulta usando o Query Explain

O Query Explain permite enviar consultas do Firestore para o back-end e receber estatísticas de desempenho detalhadas sobre a execução da consulta de back-end. Ela funciona como a operação EXPLAIN [ANALYZE] em muitos sistemas de banco de dados relacional.

As solicitações do Query Explain podem ser enviadas usando as bibliotecas de cliente do servidor do Firestore.

Os resultados do Query Explain ajudam a entender como suas consultas são executadas, mostrando ineficiências e a localização de prováveis gargalos do lado do servidor.

Explicação da consulta:

  • Fornece insights sobre a fase de planejamento da consulta para que você possa ajustar seus índices de consulta e aumentar a eficiência.
  • O uso da opção de análise ajuda a entender o custo e o desempenho por consulta, além de permitir uma iteração rápida em diferentes padrões de consulta para otimizar o uso.

Entender as opções de Query Explain: default e analyze

É possível executar as operações do Query Explain usando a opção default ou a opção analyze.

Com a opção padrão, a Query Explain planeja a consulta, mas ignora o estágio de execução. Isso retornará informações do estágio do planejador. É possível usar isso para verificar se uma consulta tem os índices necessários e entender quais índices são usados. Isso ajudará você a verificar, por exemplo, se uma consulta específica está usando um índice composto em vez de precisar cruzar em muitos índices diferentes.

Com a opção de análise, a consulta explica os dois planos e executa a consulta. Isso retorna todas as informações do planejador mencionadas anteriormente, além de estatísticas do ambiente de execução da consulta. Isso incluirá as informações de faturamento da consulta, além de insights no nível do sistema sobre a execução da consulta. É possível usar essas ferramentas para testar várias configurações de consulta e indexação para otimizar o custo e a latência.

Qual é o custo da Query Explain?

Quando você usa a opção "Query Explain" com a opção padrão, nenhuma operação de índice ou leitura é realizada. Seja qual for a complexidade da consulta, uma operação de leitura é cobrada.

Quando você usa o Query Explain com a opção de análise, as operações de índice e leitura são executadas, portanto, a consulta é cobrada como de costume. Não há cobrança extra pela atividade de análise, apenas a cobrança normal da consulta que está sendo executada.

Usar a opção "Query Explain" com a opção padrão

Você pode usar as bibliotecas de cliente para enviar uma solicitação de opção padrão.

As solicitações são autenticadas com o IAM usando as mesmas permissões para operações de consulta normais. Outras técnicas de autenticação, como o Firebase Authentication, são ignoradas. Para mais informações, consulte o guia sobre IAM para bibliotecas de cliente do servidor.

Java (Admin)

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

    
Nó (administrador)

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;

    

O formato exato da resposta depende do ambiente de execução. Os resultados retornados podem ser convertidos em JSON. Exemplo:

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

Para mais informações, consulte a referência de relatório do Query Explain.

Usar o Query Explain com a opção de análise

Use as bibliotecas de cliente para enviar uma solicitação de opção de análise.

As solicitações são autenticadas com o IAM usando as mesmas permissões para operações de consulta normais. Outras técnicas de autenticação, como o Firebase Authentication, são ignoradas. Para mais informações, consulte o guia sobre IAM para bibliotecas de cliente do servidor.

Java (Admin)

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

    
Nó (administrador)

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;

    

O exemplo a seguir mostra o objeto stats retornado, além de planInfo. O formato exato da resposta depende do ambiente de execução. A resposta de exemplo está no 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",
               }
    }

}

Para mais informações, consulte a referência de relatório do Query Explain.

Interpretar resultados e fazer ajustes

Vejamos um cenário de exemplo em que consultamos filmes por gênero e país de produção.

Para fins de ilustração, suponha o equivalente a esta consulta SQL.

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

Se usarmos a opção de análise, as métricas retornadas vão mostrar que a consulta é executada em dois índices de campo único, (category ASC, __name__ ASC) e (country ASC, __name__ ASC). Ela verifica 16.500 entradas de índice, mas retorna apenas 1.200 documentos.

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

Para otimizar o desempenho da execução da consulta, crie um índice composto (category ASC, country ASC, __name__ ASC) totalmente coberto.

Ao executar a consulta com a opção de análise novamente, podemos ver que o índice recém-criado está selecionado para essa consulta e que a consulta é executada com muito mais rapidez e eficiência.

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