Skip to main content

explain() | MongoDB | Ambiente .NET

  • explain() | MongoDB | Ambiente .NET

O que é o explain

O explain mostra como o MongoDB pretende executar, ou executou, uma operação.

Ele ajuda a responder perguntas como:

  • a query usou índice?
  • qual índice foi usado?
  • quantos documentos foram examinados?
  • quantas chaves de índice foram examinadas?
  • houve COLLSCAN?
  • a ordenação usou índice ou fez sort em memória?
  • o plano escolhido foi bom?
  • existiam planos rejeitados?

A própria documentação do MongoDB define o explain como uma forma de retornar informações sobre planos de consulta e estatísticas de execução. O resultado pode mudar entre versões do MongoDB. (MongoDB)


Como executar

Explain simples

db.users.find({ age: { $gte: 18 } }).explain()

Por padrão, usa o modo:

"queryPlanner"

Explain com estatísticas de execução

db.users.find({ age: { $gte: 18 } }).explain("executionStats")

Esse é o mais usado para análise prática de performance.


Explain com todos os planos

db.users.find({ age: { $gte: 18 } }).explain("allPlansExecution")

Esse modo mostra o plano vencedor e também informações dos planos candidatos/rejeitados. (MongoDB)


Modos do explain

ModoO que mostraQuando usar
queryPlannerMostra o plano escolhido pelo otimizador, mas não executa a query até o fimPara saber qual plano o MongoDB pretende usar
executionStatsExecuta a query e mostra estatísticas reais do plano vencedorMelhor opção para analisar performance
allPlansExecutionMostra estatísticas do plano vencedor e dos planos rejeitadosPara investigar por que um índice foi escolhido em vez de outro

A documentação informa que executionStats executa o plano vencedor até o fim e retorna estatísticas da execução, enquanto allPlansExecution também inclui informações parciais dos planos candidatos. (MongoDB)


Estrutura geral do retorno

Um retorno de explain costuma ter campos como:

{
explainVersion: "1",
queryPlanner: { ... },
executionStats: { ... },
command: { ... },
serverInfo: { ... },
serverParameters: { ... }
}

Os campos mais importantes para análise são:

queryPlanner
executionStats
winningPlan
rejectedPlans
executionStages

queryPlanner

O queryPlanner mostra como o MongoDB planejou executar a query.

Exemplo simplificado:

queryPlanner: {
namespace: "loja.users",
parsedQuery: {
age: { "$gte": 18 }
},
winningPlan: {
stage: "FETCH",
inputStage: {
stage: "IXSCAN",
indexName: "age_1"
}
},
rejectedPlans: []
}

Campos principais do queryPlanner

CampoO que significaO que analisar
namespaceBanco e collection consultadosConfirme se está analisando a collection certa
parsedQueryFiltro interpretado pelo MongoDBVeja se a query foi entendida como esperado
winningPlanPlano escolhido pelo otimizadorCampo mais importante do queryPlanner
rejectedPlansPlanos considerados, mas não escolhidosÚtil quando existem múltiplos índices
indexFilterSetIndica se há filtro de índice aplicadoNormalmente será false

winningPlan

O winningPlan é o plano vencedor.

Ele mostra a sequência de estágios que o MongoDB vai usar para buscar os dados.

Exemplo:

winningPlan: {
stage: "FETCH",
inputStage: {
stage: "IXSCAN",
indexName: "email_1",
direction: "forward"
}
}

Nesse caso:

  • IXSCAN indica que usou índice
  • email_1 foi o índice usado
  • FETCH indica que, depois de achar as chaves no índice, o MongoDB buscou os documentos completos

Principais estágios do plano

StageSignificadoInterpretação
COLLSCANCollection ScanMongoDB varreu a collection inteira
IXSCANIndex ScanMongoDB usou índice
FETCHBusca do documento completoNormal quando a query usa índice, mas precisa ler o documento
SORTOrdenação em memóriaPode indicar falta de índice adequado para sort
LIMITAplicação de limiteNormal quando usa .limit()
SKIPPula documentosPode ficar caro em paginação profunda
PROJECTION_SIMPLEProjeção simplesRetorna apenas campos específicos
PROJECTION_COVEREDQuery coberta por índiceExcelente, pois pode evitar leitura do documento
IDHACKBusca direta por _idMuito eficiente
AND_SORTEDInterseção de índices ordenadaMongoDB combinou índices
AND_HASHInterseção de índices via hashMongoDB combinou índices
SHARD_MERGEMerge de resultados de shardsAparece em ambiente sharded
EOFFim do cursor/planoNormal em alguns cenários

COLLSCAN

Exemplo:

winningPlan: {
stage: "COLLSCAN"
}

Significa que o MongoDB precisou varrer os documentos da collection.

Isso geralmente é ruim quando a collection é grande.

A documentação do MongoDB descreve COLLSCAN como uma varredura documento por documento, geralmente cara e associada a queries lentas em datasets maiores. (MongoDB)


IXSCAN

Exemplo:

winningPlan: {
stage: "FETCH",
inputStage: {
stage: "IXSCAN",
indexName: "age_1",
keyPattern: {
age: 1
}
}
}

Significa que o MongoDB usou índice.

Campos importantes dentro do IXSCAN:

CampoO que significa
indexNameNome do índice usado
keyPatternCampos que compõem o índice
directionDireção da varredura: forward ou backward
indexBoundsIntervalos do índice que foram percorridos
isMultiKeyIndica se o índice é multikey, geralmente por envolver array
multiKeyPathsMostra quais campos tornaram o índice multikey

indexBounds

Exemplo:

indexBounds: {
age: ["[18, inf.0]"]
}

Mostra a faixa do índice que foi percorrida.

No exemplo acima, o MongoDB usou o índice age_1 para buscar valores de age >= 18.

Outro exemplo:

indexBounds: {
status: ["[\"ACTIVE\", \"ACTIVE\"]"],
age: ["[18, inf.0]"]
}

Isso indica que o índice conseguiu restringir por status e depois por age.


O que analisar no indexBounds

SituaçãoInterpretação
Bounds bem restritosBom sinal
Bounds muito abertos, como [MinKey, MaxKey]Índice pode estar sendo pouco útil
Apenas o primeiro campo do índice aparece bem filtradoTalvez a ordem do índice composto não esteja ideal
Campo de sort aparece no índiceBom para evitar SORT em memória

FETCH

Exemplo:

stage: "FETCH",
inputStage: {
stage: "IXSCAN",
indexName: "email_1"
}

O FETCH indica que o MongoDB encontrou referências no índice, mas precisou buscar o documento completo na collection.

Isso é normal.

Porém, quando você quer máxima performance, pode tentar uma covered query, onde todos os campos necessários estão no índice.


Query coberta por índice

Uma query coberta ocorre quando o MongoDB consegue responder usando apenas o índice, sem buscar o documento completo.

Exemplo:

db.users.find(
{ email: "a@teste.com" },
{ email: 1, _id: 0 }
)

Com índice:

db.users.createIndex({ email: 1 })

Nesse caso, o MongoDB pode responder apenas com o índice email_1.

Sinal bom no explain:

totalDocsExamined: 0
totalKeysExamined: 1

rejectedPlans

Exemplo:

rejectedPlans: [
{
stage: "FETCH",
inputStage: {
stage: "IXSCAN",
indexName: "name_1"
}
}
]

Mostra planos que o MongoDB considerou, mas rejeitou.

Isso é útil quando você tem vários índices possíveis.


executionStats

Esse é o bloco mais importante para análise real de performance.

Exemplo:

executionStats: {
executionSuccess: true,
nReturned: 10,
executionTimeMillis: 3,
totalKeysExamined: 10,
totalDocsExamined: 10,
executionStages: { ... }
}

Campos principais do executionStats

CampoO que significaO que analisar
executionSuccessSe a execução teve sucessoDeve ser true
nReturnedQuantidade de documentos retornadosCompare com documentos examinados
executionTimeMillisTempo total em milissegundosQuanto menor, melhor
totalKeysExaminedQuantas chaves de índice foram lidasDeve ser próximo de nReturned
totalDocsExaminedQuantos documentos foram lidosDeve ser próximo de nReturned
executionStagesDetalhes por estágioUse para entender onde está o custo

A documentação recomenda usar o explain para verificar tempo de execução, uso de índice e quantidade de documentos/chaves examinadas. (MongoDB)


nReturned

Indica quantos documentos a query retornou.

Exemplo:

nReturned: 10

Esse número sozinho não diz se a query é boa.

Você precisa comparar com:

totalDocsExamined
totalKeysExamined

totalDocsExamined

Indica quantos documentos reais foram examinados.

Exemplo ruim:

nReturned: 10,
totalDocsExamined: 100000

Interpretação:

O MongoDB leu 100.000 documentos para retornar apenas 10.

Isso geralmente indica problema de índice, baixa seletividade ou filtro mal estruturado.


totalKeysExamined

Indica quantas entradas de índice foram examinadas.

Exemplo bom:

nReturned: 10,
totalKeysExamined: 10,
totalDocsExamined: 10

Exemplo ruim:

nReturned: 10,
totalKeysExamined: 50000,
totalDocsExamined: 10

Interpretação:

O MongoDB usou índice, mas precisou varrer muitas chaves para retornar poucos documentos.

Isso pode indicar:

  • índice pouco seletivo
  • ordem ruim em índice composto
  • uso inadequado de range
  • filtro que não aproveita bem o índice

Regra prática de análise

RelaçãoSituação
totalDocsExamined próximo de nReturnedBom
totalKeysExamined próximo de nReturnedBom
totalDocsExamined muito maior que nReturnedRuim
totalKeysExamined muito maior que nReturnedPode ser ruim
totalDocsExamined = 0 com resultado retornadoExcelente; provável covered query
totalKeysExamined = 0 e muitos documentos examinadosProvável COLLSCAN

Exemplo bom

executionStats: {
nReturned: 5,
totalKeysExamined: 5,
totalDocsExamined: 5,
executionTimeMillis: 1
}

Interpretação:

  • retornou 5 documentos
  • examinou 5 chaves de índice
  • examinou 5 documentos
  • índice está eficiente

Exemplo ruim

executionStats: {
nReturned: 5,
totalKeysExamined: 0,
totalDocsExamined: 200000,
executionTimeMillis: 850
}

Interpretação:

  • retornou só 5 documentos
  • não examinou índice
  • varreu 200.000 documentos
  • provavelmente fez COLLSCAN

Possível solução:

db.users.createIndex({ email: 1 })

Se a query for:

db.users.find({ email: "teste@email.com" })

executionStages

Mostra os detalhes internos da execução.

Exemplo:

executionStages: {
stage: "FETCH",
nReturned: 10,
docsExamined: 10,
inputStage: {
stage: "IXSCAN",
nReturned: 10,
keysExamined: 10,
indexName: "age_1"
}
}

Aqui você analisa cada etapa:

  • IXSCAN leu 10 chaves
  • FETCH buscou 10 documentos
  • retornou 10 documentos

Isso é um bom sinal.


Campos comuns dentro de executionStages

CampoO que significa
stageTipo do estágio executado
nReturnedQuantos documentos saíram daquele estágio
executionTimeMillisEstimateEstimativa de tempo daquele estágio
worksQuantidade de ciclos internos de trabalho
advancedQuantas vezes o estágio produziu resultado
needTimeQuantas vezes o estágio precisou continuar processando sem retornar resultado
needYieldQuantas vezes precisou ceder execução
saveStateQuantas vezes salvou estado
restoreStateQuantas vezes restaurou estado
isEOFSe o estágio chegou ao fim
docsExaminedDocumentos examinados naquele estágio
keysExaminedChaves de índice examinadas naquele estágio

executionTimeMillis

Exemplo:

executionTimeMillis: 25

Indica o tempo total de execução da query em milissegundos.


SORT

Exemplo:

stage: "SORT"

Indica que o MongoDB precisou ordenar os dados durante a execução.

Pode ser problema quando a collection é grande.

Exemplo de query:

db.users.find({ status: "ACTIVE" }).sort({ createdAt: -1 })

Índice recomendado:

db.users.createIndex({ status: 1, createdAt: -1 })

Assim o MongoDB pode filtrar por status e já retornar ordenado por createdAt.


Como analisar query com sort

Se aparecer:

stage: "SORT"

Pode indicar que o índice não está atendendo à ordenação.

Bom sinal seria algo como:

stage: "IXSCAN",
indexName: "status_1_createdAt_-1"

com ausência de SORT explícito.


command

Mostra o comando que foi analisado.

Exemplo:

command: {
find: "users",
filter: {
status: "ACTIVE"
},
sort: {
createdAt: -1
},
limit: 10
}

Use para validar se o MongoDB está analisando exatamente a query que você esperava.


serverInfo

Exemplo:

serverInfo: {
host: "localhost",
port: 27017,
version: "7.0.5"
}

Mostra informações do servidor MongoDB.

Útil para confirmar:

  • versão do MongoDB
  • host
  • porta
  • ambiente analisado

serverParameters

Mostra parâmetros internos do servidor.

Normalmente, para análise comum de CRUD e índices, você não precisa focar nesse bloco.


Explain em aggregation

Exemplo:

db.orders.explain("executionStats").aggregate([
{ $match: { status: "PAID" } },
{ $sort: { createdAt: -1 } },
{ $limit: 10 }
])

O que analisar:

  • se o $match usou índice
  • se o $sort usou índice
  • se está aparecendo COLLSCAN
  • se o pipeline está lendo documentos demais
  • se o $match está no começo do pipeline
  • se há estágios bloqueantes, como $sort, $group, $bucket, $facet

Explain com $lookup

Em pipelines com $lookup, o explain pode mostrar estatísticas específicas como:

CampoSignificado
totalDocsExaminedDocumentos examinados no lookup
totalKeysExaminedChaves de índice examinadas
collectionScansQuantidade de collection scans
indexesUsedÍndices usados
executionTimeMillisEstimateTempo estimado

A documentação informa que estatísticas de execução para $lookup aparecem com os modos executionStats ou allPlansExecution. (MongoDB)


Checklist do que analisar no explain

1. A query usou índice?

Procure por:

stage: "IXSCAN"

Bom sinal.

Procure por:

stage: "COLLSCAN"

Sinal de atenção.


2. Qual índice foi usado?

Procure por:

indexName: "nome_do_indice"

Confirme se o índice usado é o esperado.


3. Quantos documentos foram retornados?

Procure por:

nReturned

4. Quantos documentos foram examinados?

Procure por:

totalDocsExamined

Quanto mais próximo de nReturned, melhor.


5. Quantas chaves de índice foram examinadas?

Procure por:

totalKeysExamined

Quanto mais próximo de nReturned, melhor.


6. Houve sort em memória?

Procure por:

stage: "SORT"

Se aparecer, talvez falte índice para a ordenação.


7. Houve plano rejeitado melhor?

Veja:

rejectedPlans

Se houver muitos planos rejeitados, pode existir excesso de índices parecidos ou competição entre índices.


8. A query é coberta por índice?

Bom sinal:

totalDocsExamined: 0

Isso indica que o MongoDB conseguiu responder usando somente o índice.


Sinais de problema

Sinal no explainPossível problema
COLLSCANFalta de índice ou índice não aproveitado
totalDocsExamined muito maior que nReturnedBaixa seletividade ou índice ruim
totalKeysExamined muito maior que nReturnedÍndice usado, mas pouco eficiente
SORT explícitoOrdenação não atendida por índice
Muitos rejectedPlansMuitos índices concorrentes
indexBounds muito abertoÍndice pouco restritivo
executionTimeMillis altoQuery pesada, índice ruim ou ambiente sobrecarregado
$lookup com collectionScans altoFalta índice na collection estrangeira
$facet processando muitos documentosPipeline filtrando tarde demais

Exemplo prático

Query:

db.orders.find({
customerId: ObjectId("665f1e5c2c4a4e8b9a111111"),
status: "PAID"
}).sort({
createdAt: -1
}).explain("executionStats")

Índice ideal:

db.orders.createIndex({
customerId: 1,
status: 1,
createdAt: -1
})

O que você quer ver:

winningPlan: {
stage: "FETCH",
inputStage: {
stage: "IXSCAN",
indexName: "customerId_1_status_1_createdAt_-1"
}
}

E em executionStats:

nReturned: 10,
totalKeysExamined: 10,
totalDocsExamined: 10

Isso indica que:

  • o índice certo foi usado
  • a quantidade examinada está próxima da quantidade retornada
  • a ordenação provavelmente foi atendida pelo índice
  • a query está saudável

Exemplo problemático

Query:

db.orders.find({
status: "PAID"
}).sort({
createdAt: -1
}).explain("executionStats")

Resultado:

winningPlan: {
stage: "SORT",
inputStage: {
stage: "COLLSCAN"
}
},
executionStats: {
nReturned: 10,
totalKeysExamined: 0,
totalDocsExamined: 500000,
executionTimeMillis: 1200
}

Problemas:

  • fez COLLSCAN
  • examinou 500.000 documentos
  • retornou apenas 10
  • fez SORT
  • não usou índice

Possível índice:

db.orders.createIndex({
status: 1,
createdAt: -1
})

Explain no driver C#

Find com explain usando RunCommand

var command = new BsonDocument
{
{ "explain", new BsonDocument
{
{ "find", "orders" },
{ "filter", new BsonDocument("status", "PAID") },
{ "sort", new BsonDocument("createdAt", -1) }
}
},
{ "verbosity", "executionStats" }
};

var result = await database.RunCommandAsync<BsonDocument>(command);

Console.WriteLine(result.ToJson(new JsonWriterSettings
{
Indent = true
}));

Explain de aggregate no C#

var command = new BsonDocument
{
{ "explain", new BsonDocument
{
{ "aggregate", "orders" },
{ "pipeline", new BsonArray
{
new BsonDocument("$match", new BsonDocument("status", "PAID")),
new BsonDocument("$sort", new BsonDocument("createdAt", -1)),
new BsonDocument("$limit", 10)
}
},
{ "cursor", new BsonDocument() }
}
},
{ "verbosity", "executionStats" }
};

var result = await database.RunCommandAsync<BsonDocument>(command);

Console.WriteLine(result.ToJson(new JsonWriterSettings
{
Indent = true
}));

Regra de ouro

Para saber se uma query está boa, olhe principalmente para:

winningPlan.stage
winningPlan.inputStage.stage
indexName
nReturned
totalKeysExamined
totalDocsExamined
executionTimeMillis

A análise mais importante é esta:

documentos examinados ≈ documentos retornados
chaves examinadas ≈ documentos retornados

Se o MongoDB precisa examinar milhares ou milhões de documentos para retornar poucos resultados, a query provavelmente precisa de índice melhor, filtro mais seletivo ou mudança na modelagem.