Dia 06 de 15

Indexação Avançada e Performance

Olá, dev! Seja bem-vindo ao Dia 06 do seu plano de preparação para a certificação MongoDB Associate Developer. Hoje daremos um passo avançado: aprenderemos a diagnosticar o desempenho de consultas com o método explain(), aplicar a Regra ESR para desenhar índices perfeitos e construir Covered Queries para atingir performance máxima.

1. Diagnosticando Consultas com `explain()`

O método explain() revela o plano de execução escolhido pelo otimizador do MongoDB. Ele aceita três modos de verbosidade (Verbosity Modes):

// Analisando a execução real da consulta
db.pedidos.find({ status: "ENTREGUE", valor: { $gt: 500 } })
          .sort({ dataCriacao: -1 })
          .explain("executionStats")

Estágios Chave do Plano de Execução (Execution Stages):

Métricas Essenciais do `executionStats`:

Proporção Ideal: Quanto mais próximo totalKeysExamined estiver de nReturned, mais eficiente é o índice. Se totalDocsExamined for muito superior a nReturned, o índice precisa ser otimizado.

2. A Regra ESR (Equality, Sort, Range)

A Regra ESR é a diretriz mais cobrada no exame para determinar a ordem exata dos campos ao criar Índices Compostos:

  1. E — Equality (Igualdade): Posicione primeiro os campos testados por correspondência exata (ex: status: "ATIVO"). Isso reduz drasticamente o escopo de busca da B-tree.
  2. S — Sort (Ordenação): Posicione em segundo lugar os campos utilizados na ordenação (ex: sort({ dataCriacao: -1 })). Isso permite usar a ordenação nativa do índice e evita o estágio SORT em memória.
  3. R — Range (Intervalo): Posicione por último os campos que utilizam filtros de alcance ou desigualdade (ex: $gt, $lt, $in, $ne).

Exemplo de Aplicação ESR:

Dada a consulta:

db.pedidos.find({
  clienteId: 1002,          // Equality
  valorTotal: { $gte: 250 }  // Range
}).sort({ dataPedido: -1 }) // Sort

Seguindo rigorosamente a ordem E $\rightarrow$ S $\rightarrow$ R, o índice composto ideal é:

db.pedidos.createIndex({
  clienteId: 1,    // E
  dataPedido: -1,  // S
  valorTotal: 1    // R
})

3. Covered Queries (Consultas Cobertas)

Uma Covered Query é o padrão-ouro de desempenho no MongoDB. Ocorre quando 100% dos campos solicitados no filtro e na projeção da consulta fazem parte do próprio índice.

Por que a Covered Query é tão veloz?

O MongoDB responde à consulta lendo exclusivamente os dados da árvore B em memória RAM, eliminando completamente o estágio `FETCH` em disco. No `explain("executionStats")`, o campo totalDocsExamined será exatamente 0.

Condições Obrigatórias para ser uma Covered Query:

  1. Todos os campos do filtro estão no índice.
  2. Todos os campos na projeção estão no índice.
  3. O campo _id é explicitamente excluído da projeção ({ _id: 0 }), a menos que o _id faça parte das chaves do índice composto.
  4. Nenhum dos campos indexados é um array (índices Multikey não suportam consultas cobertas).
// Criando o índice cobridor
db.usuarios.createIndex({ status: 1, email: 1, nome: 1 })

// Consulta Coberta (Covered Query)
db.usuarios.find(
  { status: "ATIVO", email: "dev@empresa.com" },
  { _id: 0, email: 1, nome: 1 } // Exclusão do _id é mandatória!
)

4. Questões no Estilo Exame de Certificação

Questão 1: Considere a seguinte consulta: db.produtos.find({ categoria: "Eletrônicos", preco: { $gt: 100 } }).sort({ nome: 1 }). Qual das opções de índice composto a seguir segue estritamente a Regra ESR para máxima performance?

A) { preco: 1, nome: 1, categoria: 1 }
B) { categoria: 1, preco: 1, nome: 1 }
C) { categoria: 1, nome: 1, preco: 1 }
D) { nome: 1, categoria: 1, preco: 1 }

Resposta Correta: C. Seguindo ESR: Equality (categoria) $\rightarrow$ Sort (nome) $\rightarrow$ Range (preco), formando a chave { categoria: 1, nome: 1, preco: 1 }.

Questão 2: Um desenvolvedor executou explain("executionStats") para uma consulta e observou: totalKeysExamined: 50, totalDocsExamined: 0 e nReturned: 50. O que essa métrica indica?

A) A consulta realizou um COLLSCAN.
B) A consulta falhou por ultrapassar 32 MB em memória.
C) A consulta foi uma Covered Query (Consulta Coberta) perfeita.
D) O índice utilizado é um Multikey Index.

Resposta Correta: C. Quando totalDocsExamined === 0 e nReturned === totalKeysExamined, todos os dados foram obtidos diretamente do índice sem necessidade de estágio FETCH.