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
| Modo | O que mostra | Quando usar |
|---|---|---|
queryPlanner | Mostra o plano escolhido pelo otimizador, mas não executa a query até o fim | Para saber qual plano o MongoDB pretende usar |
executionStats | Executa a query e mostra estatísticas reais do plano vencedor | Melhor opção para analisar performance |
allPlansExecution | Mostra estatísticas do plano vencedor e dos planos rejeitados | Para 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
| Campo | O que significa | O que analisar |
|---|---|---|
namespace | Banco e collection consultados | Confirme se está analisando a collection certa |
parsedQuery | Filtro interpretado pelo MongoDB | Veja se a query foi entendida como esperado |
winningPlan | Plano escolhido pelo otimizador | Campo mais importante do queryPlanner |
rejectedPlans | Planos considerados, mas não escolhidos | Útil quando existem múltiplos índices |
indexFilterSet | Indica se há filtro de índice aplicado | Normalmente 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:
IXSCANindica que usou índiceemail_1foi o índice usadoFETCHindica que, depois de achar as chaves no índice, o MongoDB buscou os documentos completos
Principais estágios do plano
| Stage | Significado | Interpretação |
|---|---|---|
COLLSCAN | Collection Scan | MongoDB varreu a collection inteira |
IXSCAN | Index Scan | MongoDB usou índice |
FETCH | Busca do documento completo | Normal quando a query usa índice, mas precisa ler o documento |
SORT | Ordenação em memória | Pode indicar falta de índice adequado para sort |
LIMIT | Aplicação de limite | Normal quando usa .limit() |
SKIP | Pula documentos | Pode ficar caro em paginação profunda |
PROJECTION_SIMPLE | Projeção simples | Retorna apenas campos específicos |
PROJECTION_COVERED | Query coberta por índice | Excelente, pois pode evitar leitura do documento |
IDHACK | Busca direta por _id | Muito eficiente |
AND_SORTED | Interseção de índices ordenada | MongoDB combinou índices |
AND_HASH | Interseção de índices via hash | MongoDB combinou índices |
SHARD_MERGE | Merge de resultados de shards | Aparece em ambiente sharded |
EOF | Fim do cursor/plano | Normal 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:
| Campo | O que significa |
|---|---|
indexName | Nome do índice usado |
keyPattern | Campos que compõem o índice |
direction | Direção da varredura: forward ou backward |
indexBounds | Intervalos do índice que foram percorridos |
isMultiKey | Indica se o índice é multikey, geralmente por envolver array |
multiKeyPaths | Mostra 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ção | Interpretação |
|---|---|
| Bounds bem restritos | Bom sinal |
Bounds muito abertos, como [MinKey, MaxKey] | Índice pode estar sendo pouco útil |
| Apenas o primeiro campo do índice aparece bem filtrado | Talvez a ordem do índice composto não esteja ideal |
| Campo de sort aparece no índice | Bom 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
| Campo | O que significa | O que analisar |
|---|---|---|
executionSuccess | Se a execução teve sucesso | Deve ser true |
nReturned | Quantidade de documentos retornados | Compare com documentos examinados |
executionTimeMillis | Tempo total em milissegundos | Quanto menor, melhor |
totalKeysExamined | Quantas chaves de índice foram lidas | Deve ser próximo de nReturned |
totalDocsExamined | Quantos documentos foram lidos | Deve ser próximo de nReturned |
executionStages | Detalhes por estágio | Use 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ção | Situação |
|---|---|
totalDocsExamined próximo de nReturned | Bom |
totalKeysExamined próximo de nReturned | Bom |
totalDocsExamined muito maior que nReturned | Ruim |
totalKeysExamined muito maior que nReturned | Pode ser ruim |
totalDocsExamined = 0 com resultado retornado | Excelente; provável covered query |
totalKeysExamined = 0 e muitos documentos examinados | Prová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:
IXSCANleu 10 chavesFETCHbuscou 10 documentos- retornou 10 documentos
Isso é um bom sinal.
Campos comuns dentro de executionStages
| Campo | O que significa |
|---|---|
stage | Tipo do estágio executado |
nReturned | Quantos documentos saíram daquele estágio |
executionTimeMillisEstimate | Estimativa de tempo daquele estágio |
works | Quantidade de ciclos internos de trabalho |
advanced | Quantas vezes o estágio produziu resultado |
needTime | Quantas vezes o estágio precisou continuar processando sem retornar resultado |
needYield | Quantas vezes precisou ceder execução |
saveState | Quantas vezes salvou estado |
restoreState | Quantas vezes restaurou estado |
isEOF | Se o estágio chegou ao fim |
docsExamined | Documentos examinados naquele estágio |
keysExamined | Chaves 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
$matchusou índice - se o
$sortusou índice - se está aparecendo
COLLSCAN - se o pipeline está lendo documentos demais
- se o
$matchestá 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:
| Campo | Significado |
|---|---|
totalDocsExamined | Documentos examinados no lookup |
totalKeysExamined | Chaves de índice examinadas |
collectionScans | Quantidade de collection scans |
indexesUsed | Índices usados |
executionTimeMillisEstimate | Tempo 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 explain | Possível problema |
|---|---|
COLLSCAN | Falta de índice ou índice não aproveitado |
totalDocsExamined muito maior que nReturned | Baixa seletividade ou índice ruim |
totalKeysExamined muito maior que nReturned | Índice usado, mas pouco eficiente |
SORT explícito | Ordenação não atendida por índice |
Muitos rejectedPlans | Muitos índices concorrentes |
indexBounds muito aberto | Índice pouco restritivo |
executionTimeMillis alto | Query pesada, índice ruim ou ambiente sobrecarregado |
$lookup com collectionScans alto | Falta índice na collection estrangeira |
$facet processando muitos documentos | Pipeline 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.