Curso
Em qualquer banco de dados, incluindo o MongoDB, os índices são parte crucial do desenvolvimento. Sem eles, conforme o banco cresce, o desempenho tende a cair junto. Com índices adequados, à medida que os dados aumentam, o acesso continua eficiente.
Neste artigo, vamos explorar os diferentes tipos de índices disponíveis no MongoDB, compartilhar algumas dicas para ter a melhor experiência e mostrar como garantir que você está, de fato, aproveitando os índices que cria. Se você ainda está aprendendo MongoDB, vale conferir o curso Introduction to MongoDB.
Onde os índices do MongoDB são importantes e por quê?
Quando falamos de índices, falamos de performance. Índices fazem a diferença entre vasculhar documento por documento de uma coleção para ver se bate com a consulta, ou ir direto a um subconjunto específico que corresponde ao que você busca. Se a coleção é pequena e há poucos documentos para analisar, o desempenho será bom de qualquer forma. Mas, se temos 100.000 documentos e apenas 100 correspondem à nossa consulta, vai levar tempo varrer todos os 100.000 para encontrar esses 100, caso não exista um índice baseado nesses campos.
Há vários tipos de índices no MongoDB:
- Campo único: um índice em um campo específico do documento. O índice padrão do campo
_idé um exemplo de índice de campo único. - Composto: um índice baseado em vários campos do documento, em que a ordem dos campos na consulta e no índice é importante.
- Multichave: índice usado em campos do tipo array.
- Curinga (wildcard): índice que dá suporte a campos com nomes ligeiramente diferentes entre documentos. Esse tipo tende a ter performance inferior a outros índices.
- Geoespacial: índice para melhorar o desempenho de consultas geoespaciais que usam dados de latitude e longitude.
- Hashed: índice mais indicado para uso com chaves de shard.
Para saber mais sobre os tipos de índice, consulte a documentação do MongoDB.
Na maior parte do tempo, você vai usar índices de campo único, compostos ou multichave, mas, no fim do dia, tudo depende do tipo de aplicação, consultas e dados.
Índices são importantes em qualquer interação que busca documentos. Isso não se limita às operações find ou findOne. Ao atualizar e excluir documentos, você também usa critérios de correspondência que o banco aplica para encontrar os documentos antes de atualizar ou remover.
Os operadores find e findOne não são os únicos que se beneficiam de índices. Quando você usa o operador $match como primeira etapa do pipeline de agregação, um índice acelera drasticamente a execução.
Dicas e truques de indexação no MongoDB para ter sucesso
Criar índices aleatoriamente pode ser tão ineficiente quanto não usar índice nenhum. Em vez disso, vale considerar alguns pontos que ajudam a construir uma aplicação muito mais eficiente.
Equality, sort, range (ESR) para índices compostos
A regra de ouro ao criar um índice composto é entender como cada campo será usado na sua consulta e garantir que siga a boa prática ESR.
O que significa ESR? Vamos por partes:
- Equality (igualdade): campos que serão comparados por igualdade exata, não de forma aproximada. Por exemplo, buscar o nome exato de alguém na sua consulta é uma etapa de igualdade.
- Sort (ordenação): campos usados para ordenar em ordem ascendente ou descendente.
- Range (intervalo): campos que serão filtrados por intervalo, não por igualdade exata. Por exemplo, usar os operadores
$ltou$gtretorna documentos dentro de um intervalo, e não uma correspondência específica.
Para visualizar melhor, veja a consulta a seguir:
db.employee.find(
{
"department": "Developer Relations",
"salary": {
"$lt": 80000
}
}
).sort(
{
"name": 1
}
);
A consulta acima faz correspondência por igualdade em department, por intervalo em salary e ordena o resultado por name.
Seguindo a regra ESR, o índice ficaria assim:
db.employee.createIndex({
"department": 1,
"name": 1,
"salary": 1
});
Seguir ESR evita ordenações em memória e reduz o espaço de busca, garantindo uma execução de consulta mais performática.
Consultas cobertas (covered queries)
Em geral, indexação cria um subconjunto menor de documentos para varrer durante a consulta e, com isso, melhora o desempenho. Mas, se você quer ir além, explore as consultas cobertas. Em uma covered query, todos os resultados vêm direto do índice, sem varrer nenhum documento da coleção.
Há alguns pontos de atenção que podem viabilizar ou não consultas cobertas na sua aplicação:
- Os critérios de filtro e a ordenação precisam corresponder exatamente ao que seu índice abrange.
- Os campos solicitados no resultado também precisam estar no índice.
- Em ambientes shard, a shard key deve fazer parte do índice.
Então, o que isso significa na prática?
Veja o índice a seguir, por exemplo:
db.department.createIndex({
"country": 1,
"salary": 1
});
No exemplo acima, queremos indexar pelos campos country e salary. Em seguida, podemos executar a consulta abaixo:
db.department.find(
{
"country": "US"
},
{
"country": 1,
"salary": 1
}
);
Essa consulta, com projeção, usará nosso índice, mas não será uma consulta coberta. O campo _id é retornado por padrão no MongoDB e, neste exemplo, não foi incluído no índice. Então podemos alterar o índice ou a consulta. Alterando a consulta, ficaria assim:
db.department.find(
{
"country": "US"
},
{
"country": 1,
"salary": 1,
"_id": 0
}
);
Lembre-se: para usar uma covered query, o índice precisa ser válido e os campos solicitados devem estar no índice. Caso contrário, você estará apenas encontrando documentos guiado pelo índice, não diretamente no índice.
Você pode verificar se uma consulta coberta está sendo usada com o argumento executionStats no operador explain mencionado mais abaixo. O valor de totalDocsExamined indica o que foi utilizado. Se for zero, você está usando uma covered query.
Reduzindo ou eliminando índices desnecessários
Índices não são “de graça” em termos de recursos do servidor. Cada índice precisa ser mantido e isso consome CPU, RAM e disco. Ao criar mais índices, você aumenta o overhead, o que pode gerar custos maiores ou reduzir a performance em servidores subdimensionados. Essa queda pode afetar gravações e exclusões, não só leituras, pois o MongoDB estará consumindo mais recursos.
O ideal é criar índices apenas para consultas de alta frequência. Aquela consulta mensal realmente precisa de índice? Provavelmente não. E a consulta que roda a cada 10 segundos? Com certeza sim!
Veja mais boas práticas de indexação no blog do MongoDB.
Seu índice no MongoDB está funcionando?
Só porque você criou um índice no MongoDB não significa que a consulta está de fato usando-o. No MongoDB, dá para inspecionar qual índice está sendo utilizado em cada consulta.
Considere o índice a seguir:
db.people.createIndex({
"name": 1,
"is_active": 1
});
No exemplo acima, estamos indexando os campos name e is_active. Agora, vamos executar a consulta abaixo:
db.people.find({
"is_active": true
});
Essa consulta roda com ou sem índice válido e, se o dataset for pequeno, será rápida de qualquer forma. Porém, se quisermos checar se o índice está sendo usado, podemos ajustar a consulta:
db.people.find({
"is_active": true
}).explain();
Agora usamos o método explain para obter o plano de execução da consulta. No plano, podemos ver algo como:
winningPlan: {
isCached: false,
stage: 'COLLSCAN',
filter: { is_active: { '$eq': true } },
direction: 'forward'
}
Para essa consulta, o MongoDB usou um "COLLSCAN", ou seja, não utilizou nosso índice, mesmo contendo um dos campos indexados. Se mudarmos a consulta para algo assim:
db.people.find({
"name": "Nic Raboy",
"is_active": true
}).explain();
O resultado do explain muda:
winningPlan: {
isCached: false,
stage: 'FETCH',
inputStage: {
stage: 'IXSCAN',
keyPattern: { name: 1, is_active: 1 },
indexName: 'name_1_is_active_1',
isMultiKey: false,
multiKeyPaths: { name: [], is_active: [] },
isUnique: false,
isSparse: false,
isPartial: false,
indexVersion: 2,
direction: 'forward',
indexBounds: {
name: [ '["Nic Raboy", "Nic Raboy"]' ],
is_active: [ '[true, true]' ]
}
}
},
Dessa vez, o plano indica um "IXSCAN" e especifica o nome do índice usado, name_1_is_active_1. O nome foi definido automaticamente na criação do índice.
Para uma operação de update, o processo é semelhante, com a diferença de onde o explain é aplicado.
Uma operação de update poderia ser assim:
db.people.explain().update(
{
“name”: “Nic Raboy”,
“Is_active”: true
},
{
“$set”: { “is_active”: false }
}
);
Note que, no exemplo acima, o explain vem antes da função update.
Se o índice esperado estiver sendo usado no update, você verá "IXSCAN" junto com o nome do índice. Saiba mais sobre operações de update e uso de índices.
Outra forma de obter detalhes do plano de execução é usar runCommand no banco. Embora menos “elegante” visualmente, é bem poderoso e fica assim:
db.runCommand({
"explain": {
"update": "people",
"updates": [
{
"q": {
"name": "Nic Raboy",
"is_active": true
},
"u": {
"$set": { "is_active": false }
}
}
]
},
"verbosity": "allPlansExecution"
});
O comando acima funciona como o update com explain.
Se você não tem confiança na criação de índices ou percebe que as consultas estão mais lentas do que o esperado, vale investigar os detalhes de execução.
Conclusão
Você acabou de ver algumas boas práticas de indexação no MongoDB. Embora não tenhamos explorado todos os cenários possíveis, isso serve como ponto de partida para garantir que seus usuários tenham a melhor experiência ao trabalhar com seus dados.
Reforçando alguns pontos:
- Siga a regra equality, sort, range (ESR) sempre que possível.
- Se puder, tente usar covered queries.
- Não saia criando índices a torto e a direito. Entenda quais consultas serão mais usadas e quanto dado elas trazem.
- Apoie-se no operador
explainpara testar seus índices e sua performance.
Os pontos acima não formam uma lista exaustiva de boas práticas de indexação para melhorar a performance da sua aplicação. Há outros fatores, como cardinalidade.
Para mais ajuda com indexação, consulte a documentação do MongoDB. Se você usa um plano pago no MongoDB Atlas, considere as ferramentas de performance adviser com recomendações de índices e consultas.
Perguntas frequentes sobre indexação no MongoDB
Quantos índices devo criar em uma coleção do MongoDB?
Depende. Você precisa encontrar um equilíbrio que funcione para o seu caso, mas considere indexar apenas consultas executadas com frequência, e não esporadicamente.
Criei alguns índices, mas minhas consultas continuam lentas. Por quê?
Você tem certeza de que suas consultas estão realmente usando os índices que criou? Use o operador explain ou algumas das ferramentas de recomendação no MongoDB Atlas para diagnosticar.
Para a regra ESR, a ordem dos campos no índice precisa ser exatamente igualdade, depois ordenação e, por último, intervalo?
Embora o MongoDB às vezes consiga usar um índice fora de ordem, ele é mais eficiente quando a ordem das chaves do índice acompanha a sequência da consulta: primeiro filtros de igualdade, depois ordenação e, por fim, intervalo.
Quais são os diferentes modos do explain?
Por padrão, explain() roda no modo "queryPlanner", mas existem outros modos: "executionStats" e "allPlansExecution".

Nic Raboy é Líder de Relações com Desenvolvedores na MongoDB, onde lidera uma equipe de desenvolvedores Python, Java, C# e PHP que criam conteúdo incrível para ajudar os desenvolvedores a serem bem-sucedidos na inclusão do MongoDB em seus projetos. Ele tem experiência com Golang e JavaScript e escreve com frequência sobre muitas de suas aventuras de desenvolvimento.


