MongoDB: Tipos de índice e recursos avançados
Última atualização: 2026-08-26
Recursos avançados de indexação — Domine as regras de TTL, indexação parcial e ESR para criar um sistema de indexação pronto para produção.
1. O que você vai aprender
- Índice exclusivo (único)
- Índice esparso
- Índice TTL (expiração automática)
- parcial: índice parcial
- Regra ESR (Igualdade/Ordenação/Intervalo)
- Melhores práticas para o projeto de índices
graph TB
A[Index Types] --> B[unique<br/>Unique Index]
A --> C[sparse<br/>Sparse Index]
A --> D[TTL<br/>Automatic Expiration]
A --> E[partial<br/>Selected Indexes]
A --> F[compound<br/>Composite Index]
B --> B1[Ensure Uniqueness<br/>Repeated Throw Errors]
C --> C1[Skip null<br/>Space-saving]
D --> D1[Automatic Deletion<br/>Scheduled Cleanup]
E --> E1[Indexed subset only<br/>Performance + Savings]
style D fill:#d4edda
style E fill:#d4edda
2. Índice único (unique)
Explicação do conceito: Um índice de exclusividade garante que os valores no campo indexado sejam únicos em toda a coleção, proporcionando garantia de integridade dos dados no nível do banco de dados. Ao contrário da validação no nível do aplicativo, um índice de exclusividade é aplicado pelo mecanismo do banco de dados — valores duplicados são rejeitados, independentemente de qual cliente os grave.
Como funciona: Um índice único impõe restrições de exclusividade dentro de uma árvore B+. Sempre que ocorre uma inserção ou atualização, o mecanismo verifica primeiro se já existe no índice uma chave com o mesmo valor; caso exista, ele lança uma exceção E11000 duplicate key error. Um índice único composto exige que a combinação de campos seja única (campos individuais podem estar duplicados).
Casos de uso:
- Endereço de e-mail do usuário, número de celular (identificador único global)
- Número de SKU (identificador exclusivo do produto)
- Exclusividade composta: por exemplo,
(userId, productId)garante que cada usuário possa avaliar o mesmo produto apenas uma vez. - Não adequado: Campos que permitem valores duplicados (como
category,status)
| Dimensão | Índice regular | Índice exclusivo |
|---|---|---|
| Restrições de valor | Duplicatas permitidas | Duplicatas não permitidas |
| Inserir e verificar | Atualizar apenas a árvore B+ | Atualizar a árvore B+ e verificar a exclusividade |
| Desempenho de gravação | Teste de desempenho | Ligeiramente mais lento (+5% de sobrecarga de paridade) |
| Mensagem de erro | Nenhuma | E11000: erro de chave duplicada |
| Parcialmente exclusivo | Não suportado | Implementado usando partialFilterExpression |
// === Create a unique index ===
db.users.createIndex({ email: 1 }, { unique: true });
// email Field values must be unique.
// === Composite Unique Index ===
db.products.createIndex({ sku: 1, variant: 1 }, { unique: true });
// (sku, variant) The combination must be unique.
// === Unique Index + Selected Fields ===
db.users.createIndex(
{ email: 1 },
{ unique: true, partialFilterExpression: { email: { $exists: true } } }
);
// Only for existing email Apply a unique constraint to the field's documentation
Análise dos pontos-chave:
- Um índice único permite que o valor
nullexista, mas só pode haver umnullem todo o conjunto (o valor nulo é considerado o mesmo valor). - O uso de
partialFilterExpressionpermite que a restrição de exclusividade se aplique apenas a alguns documentos, resolvendo o problema da exclusividade nula. - Antes de criar um índice único, a coleção não deve conter nenhum valor duplicado; caso contrário, a criação falhará.
3. Índices esparsos
Explicação do conceito: Um índice esparso indexa apenas os documentos que possuem o campo indexado e nos quais esse campo não é nulo, ignorando os documentos em que o campo está ausente ou é nulo. Um índice comum indexa todos os documentos (tratando os campos ausentes como nulos), enquanto um índice esparso economiza espaço ao excluir entradas inválidas.
Como funciona: Ao criar um índice esparso, o mecanismo verifica se o campo indexado existe e se não é nulo após a inserção de um documento; ele insere uma entrada de índice na árvore B+ apenas se essas condições forem atendidas. Durante uma consulta, se for utilizado um índice esparso, os resultados não incluirão documentos com campos ausentes (pois eles não constam no índice).
Casos de uso:
- Campos opcionais (como
discountemiddleName); esse campo está ausente na maioria dos documentos - Índice único + esparso = permite que vários documentos não tenham esse campo, mantendo, ainda assim, a exclusividade dos valores existentes
- Não adequado: consultas que exigem documentos com campos ausentes (os índices esparsos não conseguem abranger esses documentos)
graph LR
subgraph "Collection of Documents"
D1["{sku:'A', discount:10}"]
D2["{sku:'B'}"]
D3["{sku:'C', discount:null}"]
D4["{sku:'D', discount:20}"]
end
subgraph "Regular Index"
I1["A→D1, B→D2, C→D3, D→D4"]
end
subgraph "Sparse Index"
I2["10→D1, 20→D4<br/>(only 2 entries)"]
end
style I2 fill:#d4edda
| Cenário | Recomendação | Motivo |
|---|---|---|
| Frequentemente faltam campos (por exemplo, campos opcionais) | Índice esparso | Economiza espaço ao indexar apenas documentos com valores |
| Os campos devem ter valores | Índice regular | Não há necessidade de pular; o índice esparso não oferece nenhuma vantagem |
| Exclusivo + Permite vários valores nulos | Índice exclusivo esparso | Os valores nulos não são incluídos nas verificações de exclusividade |
| As consultas devem retornar documentos nulos | Índice regular | Os índices esparsos omitem documentos nulos |
// === Sparse Index:Skip null Field ===
db.products.createIndex({ discount: 1 }, { sparse: true });
// Index only discount Field Documentation
// === Sparse Index vs Regular Index ===
// Regular Index:All documents are indexed(Including null)
// Sparse Index:Only non- null Indexing Documents
Análise dos pontos-chave:
- Os índices esparsos não abrangem documentos com campos ausentes;
find({discount: null})não retornará documentos nos quais esse campo esteja ausente. - Combinação “esparsa + única”: permite que vários documentos não tenham esse campo, garantindo, ao mesmo tempo, que os valores existentes sejam únicos
- Os índices parciais são um superconjunto dos índices esparsos (e são mais flexíveis); recomenda-se usar os índices parciais em primeiro lugar.
4. Índices TTL (expiração automática)
Explicação do conceito: O índice TTL (Time-To-Live) é o mecanismo automático de expiração e exclusão do MongoDB, que exclui automaticamente os documentos que excederam um período de tempo especificado com base em um campo de data. Não requer limpeza manual nem tarefas agendadas; o mecanismo do banco de dados lida com isso automaticamente em segundo plano — tornando-o uma ferramenta poderosa para gerenciamento de sessões e limpeza de logs.
Como funciona: O índice TTL adiciona uma thread de limpeza em segundo plano à árvore B+. Essa thread é executada a cada 60 segundos, verifica os campos de data no índice, calcula currentTime - fieldValue > expireAfterSeconds e exclui todos os documentos expirados. A operação de exclusão em si gera sobrecarga de gravação, e um grande número de documentos expirados pode causar um aumento repentino na pressão de gravação.
sequenceDiagram
participant App as Applications
participant TTL as TTLBackground Threads
participant IX as TTLIndexB+Tree
participant DOC as Collection of Documents
App->>DOC: Insert {token:'abc', createdAt: T1}
DOC->>IX: Index Entries createdAt=T1
Note over TTL: Every 60 seconds
loop Every 60 seconds
TTL->>IX: Scan createdAt values
IX-->>TTL: Back to All Entries
TTL->>TTL: Calculate the current time - createdAt
alt Expired ( > expireAfterSeconds )
TTL->>DOC: Delete Expired Documents
DOC->>IX: Delete the corresponding index entry
else Not expired
Note over TTL: Skip
end
end
Casos de uso:
| Cenário | expireAfterSeconds | Valor típico |
|---|---|---|
| Sessão do usuário | 30 minutos | 1.800 |
| Código de verificação | 10 minutos | 600 |
| Dados de registro | Retidos por 90 dias | 7.776.000 |
| Token temporário | 1 hora | 3.600 |
| Log de limitação de taxa | 1 dia | 86.400 |
⚠️ Limite de TTL:
| Limitação | Descrição | Solução |
|---|---|---|
| Não pode ser um índice composto | O TTL só pode ser baseado em um único campo de data | Para condições compostas, use um índice parcial + limpeza no nível do aplicativo |
| O campo deve ser do tipo Data | Números/cadeias de caracteres não são suportados | Use new Date() para armazenar a hora |
| Atraso na exclusão | A thread em segundo plano faz uma verificação a cada 60 segundos | Atraso máximo de 60 segundos; o tempo não é exato |
| Não se garante que a exclusão seja precisa | Um grande número de documentos vencidos pode ser excluído em lotes | Garanta a tolerância a falhas na camada de negócios |
// === Create TTL Index(30 Automatically Deleted by the Queen)===
db.sessions.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 30 * 24 * 60 * 60 }
);
// === Edit TTL ===
db.runCommand({
collMod: 'sessions',
index: { keyPattern: { createdAt: 1 }, expireAfterSeconds: 7 * 24 * 60 * 60 }
});
// === Cancel TTL(Set as false)===
db.runCommand({
collMod: 'sessions',
index: { keyPattern: { createdAt: 1 }, expireAfterSeconds: -1 }
});
Cenários típicos:
- Sessão do usuário (expira após 30 minutos)
- Código de verificação (validade de 10 minutos)
- Dados de registro (armazenados por 90 dias)
- Token temporário (vence em 1 hora)
5. Índices parciais
Explicação do conceito: Um índice parcial indexa apenas os documentos que atendem aos critérios de filtragem; trata-se de uma versão aprimorada do índice esparso. Ao utilizar partialFilterExpression para especificar quais documentos serão incluídos no índice, é possível economizar espaço e melhorar a precisão do índice, tornando-o um dos métodos de otimização de índice mais recomendados para ambientes de produção.
Como funciona: Ao criar um índice parcial, o mecanismo insere na árvore B+ apenas os documentos que satisfazem partialFilterExpression. Durante uma consulta, o otimizador selecionará esse índice somente se as condições da consulta “abrangerem” a partialFilterExpression (ou seja, se as condições da consulta forem um subconjunto ou equivalentes às condições do filtro); caso contrário, ele não será utilizado.
Casos de uso:
- Indexar apenas “Itens à venda” (
isActive: true, stock: {$gt: 0}), ignorando itens descontinuados e fora de estoque - Indexar apenas “usuários verificados” (
emailVerified: true) e ignorar os usuários não verificados - As restrições de exclusividade se aplicam apenas a determinados documentos (por exemplo, os títulos de artigos publicados devem ser exclusivos, enquanto títulos duplicados são permitidos em rascunhos)
graph TB
subgraph "Gathering(10000Document)"
A[All Documents<br/>10000 items]
end
subgraph "Regular Index"
B[Index Entries<br/>10000 items<br/>~10MB]
end
subgraph "PartialIndex<br/>isActive=true, stock>0"
C[Index Entries<br/>3000 items<br/>~3MB]
end
A --> B
A --> C
style C fill:#d4edda
| Critérios de comparação | Índice regular | Índice parcial | Índice esparso |
|---|---|---|---|
| Condições de filtragem | Nenhuma | Suporta $eq/$gt/$gte/$lt/$lte/$exists/$type/$and | Apenas existência do campo |
| Economia de espaço | 0% | 30–70% | Depende da proporção de dados ausentes |
| Flexibilidade | Referência | ⭐⭐⭐ Mais flexível | ⭐ Mais limitado |
| Restrições de consulta | Nenhuma | As condições da consulta devem corresponder às condições do filtro | As consultas devem incluir condições de campo |
| Recomendado | Padrão | ✅ Preferível para produção | Apenas para cenários simples |
| Expressão | Suporte |
|---|---|
$eq / $gt / $gte / $lt / $lte |
✅ |
$exists: true |
✅ |
$type |
✅ |
$and |
✅ |
$or / $in / $nin |
❌ |
// === Selected Indexes:Index only documents that meet the criteria ===
db.products.createIndex(
{ category: 1, price: 1 },
{
partialFilterExpression: {
isActive: true,
stock: { $gt: 0 }
}
}
);
// Index only products currently for sale(isActive=true, stock>0)
// === Space-Saving Indexes ===
// Regular Index:1 Index all documents
// Partial Index: Only index 3000 products currently for sale (Saves 70% space)
▶ Exemplo 1: Indexação parcial na prática
// ShopHub:Index only products that are for sale and in stock,Savings 70% Index Space
db.products.insertMany([
{ sku: 'A001', category: 'Electronics', price: 599, isActive: true, stock: 50 },
{ sku: 'A002', category: 'Electronics', price: 299, isActive: false, stock: 0 },
{ sku: 'A003', category: 'Books', price: 29, isActive: true, stock: 100 },
{ sku: 'A004', category: 'Books', price: 49, isActive: true, stock: 0 }
]);
// Partial Index only isActive=true AND stock>0 the document(A001, A003)
db.products.createIndex(
{ category: 1, price: 1 },
{ partialFilterExpression: { isActive: true, stock: { $gt: 0 } } }
);
// The query must include filter Conditions for a hit
db.products.find({
category: 'Electronics',
isActive: true,
stock: { $gt: 0 },
price: { $gte: 100 }
}).explain();
// winningPlan.stage: IXSCAN ✅
// Missing filter Conditions → Missed
db.products.find({ category: 'Electronics', price: { $gte: 100 } }).explain();
// winningPlan.stage: COLLSCAN(Do not use partial Index)
6. Regras do ESR
Equalidade → Sorteio → Range: a regra de ouro para a ordem dos campos em um índice composto.
Explicação do conceito: A regra ESR é a regra mais importante no projeto de índices do MongoDB. Ela especifica a ordem ideal dos campos em um índice composto: as condições de igualdade vêm primeiro, os campos de classificação no meio e as condições de intervalo por último. A violação da regra ESR pode resultar em uma redução significativa na eficiência do índice ou até mesmo torná-lo ineficaz.
Como funciona:
- Os campos de igualdade são classificados primeiro: as condições de igualdade podem reduzir rapidamente o espaço de busca de N para K (K << N), proporcionando o ponto de partida mais preciso para os campos subsequentes
- Campo de ordenação centralizado: como os índices são inerentemente ordenados, se o campo de ordenação vier imediatamente após o campo de igualdade, os resultados da consulta já estarão ordenados na ordem do índice, eliminando a necessidade de ordenação na memória (evitando a etapa SORT).
- Coloque o campo de intervalo por último: as consultas de intervalo examinam um intervalo contíguo do índice, e os campos subsequentes dentro desse intervalo não podem se beneficiar da ordem de classificação do índice; portanto, o campo de intervalo deve ser colocado por último.
graph TB
subgraph "ESR Index {status, createdAt, price}"
A["Equality: status='paid'<br/>→ Locate all paid Document"] --> B["Sort: createdAt: -1<br/>→ The index is sorted,Memory-Free Sorting"]
B --> C["Range: price >= 100<br/>→ Range Scan,Interrupt Subsequent Fields"]
end
subgraph "Counterexample:RSE Index {price, status, createdAt}"
D["Range: price >= 100<br/>→ Scan a large number of index entries"] --> E["Sort: status<br/>→ Memory-based sorting required"]
E --> F["Equality: createdAt<br/>→ The index can no longer be used"]
end
style A fill:#d4edda
style B fill:#cce5ff
style C fill:#fff3cd
style D fill:#f8d7da
style E fill:#f8d7da
style F fill:#f8d7da
Tabela comparativa das regras do ESR:
| Ordem | Tipo | Exemplo | Função |
|---|---|---|---|
| 1º | Igualdade | category: 'Electronics' |
Filtrar rapidamente as opções |
| 2º | Igualdade | isActive: true |
Restringir ainda mais o escopo |
| 3º | Classificação | sort: { createdAt: -1 } |
Utiliza a natureza ordenada do índice para evitar a classificação |
| 4º | Intervalo | price: { $gte: 100 } |
Varredura de intervalo, colocar no final |
Consequências da violação do ESR:
| Ordem incorreta | Consequências | Explicação |
|---|---|---|
| Intervalo anterior à igualdade | A varredura do intervalo é muito ampla | totalKeysExamined é muito maior que nReturned |
| Classificação por intervalo | Não é possível usar um índice para classificação | Ocorre uma etapa SORT (classificação na memória) |
| Ignorar a igualdade e ordenar diretamente | Ordenação sem ponto de partida exato | Varredura completa do índice + ordenação |
// === Search:Equivalence Filtering + Sort + Scope ===
db.products.find({
category: 'Electronics', // Equality
isActive: true // Equality
}).sort({ createdAt: -1 }) // Sort
.limit(20);
// Price Range
db.products.find({
category: 'Electronics',
createdAt: { $gte: new Date('2026-01-01') } // Range
}).sort({ rating: -1 });
// === Best Index ===
db.products.createIndex({
category: 1, // E - Equivalent
isActive: 1, // E - Equivalent
createdAt: -1, // S - Sort(Index Direction Matching)
rating: -1 // R - Scope(Save it for last)
});
| Ordem | Tipo | Exemplo |
|---|---|---|
| 1º | Igualdade | category: 'Electronics' |
| 2º | Igualdade | isActive: true |
| 3º | Classificação | sort: { createdAt: -1 } |
| 4º | Faixa | createdAt: { $gte: ... } |
▶ Exemplo 2: Uma comparação prática das regras do ESR
// TechCorp Order System:Comparison ESR Correct vs Execution Plan with Incorrect Ordering
db.orders.insertMany([
{ userId: 'user_001', status: 'paid', createdAt: new Date('2026-07-01'), total: 100 },
{ userId: 'user_002', status: 'pending', createdAt: new Date('2026-07-02'), total: 200 },
{ userId: 'user_001', status: 'paid', createdAt: new Date('2026-07-03'), total: 50 }
]);
// ❌ Index Errors: Range before Sort
db.orders.createIndex({ userId: 1, total: 1, status: 1, createdAt: -1 }, { name: 'idx_wrong' });
db.orders.find({ userId: 'user_001', status: 'paid' })
.sort({ createdAt: -1 }).explain('executionStats');
// appear SORT stage(Memory Sorting),totalKeysExamined much greater than nReturned
// ✅ Correct Indexing:ESR Order
db.orders.dropIndex('idx_wrong');
db.orders.createIndex({ userId: 1, status: 1, createdAt: -1, total: 1 }, { name: 'idx_esr' });
db.orders.find({ userId: 'user_001', status: 'paid' })
.sort({ createdAt: -1 }).explain('executionStats');
// No SORT stage, totalKeysExamined ≈ nReturned
7. Melhores práticas para o projeto de índices
Explicação do conceito: O projeto de índices não é uma decisão técnica isolada, mas sim uma análise abrangente de compromissos baseada em padrões de consulta, características dos dados e requisitos de negócios. Um bom projeto de índices pode agilizar significativamente as consultas, enquanto um projeto inadequado desperdiça espaço e torna as gravações mais lentas.
Princípios de Design:
- Projeto baseado em consultas — Primeiro, analise
explain()e o log de consultas lentas; em seguida, crie índices - Prioridade ESR — A ordem dos campos em um índice composto segue a sequência Igualdade → Classificação → Intervalo
- Priorize a alta seletividade — Campos com muitos valores únicos (como
userId) são mais adequados para indexação do que campos com baixa seletividade (comoisActive) - Cobertura de índice — Considere a cobertura de índice para consultas de alta frequência, a fim de evitar consultas à tabela
- Limpeza regular — Use
$indexStatspara localizar e excluir índices não utilizados
graph TB
A[Analyzing Query Patterns<br/>Slow Query Log] --> B[Identifying High-Frequency Queries]
B --> C{Query Type?}
C -->|Equal Value Query| D[Single field/Unique Index]
C -->|Multiple Condition Combinations| E[Composite Index ESR]
C -->|Sort+Filter| F[ESR Composite Index]
C -->|Show only a few fields| G[Index Coverage]
B --> H[Evaluating Options]
H -->|High selectivity| I[✅ Create an index]
H -->|Low selectivity| J[❌ Do not build / use partial]
I --> K[Create+Verification<br/>explain]
K --> L[Monitoring Utilization<br/>$indexStats]
L --> M{Low utilization rate?}
M -->|Yes| N[Delete Index]
M -->|No| O[Retain]
style D fill:#d4edda
style E fill:#d4edda
style F fill:#d4edda
style G fill:#d4edda
(1) Abordagem recomendada
// ✅ Recommendations 1:Single-Field Indexes Cover High-Frequency Queries
db.products.createIndex({ sku: 1 });
// ✅ Recommendations 2:Composite indexes follow ESR Principles
db.orders.createIndex({ userId: 1, status: 1, createdAt: -1 });
// ✅ Recommendations 3:TTL Automatic Cleanup
db.logs.createIndex({ createdAt: 1 }, { expireAfterSeconds: 30 * 86400 });
// ✅ Recommendations 4:Space-Saving Indexes
db.products.createIndex(
{ category: 1 },
{ partialFilterExpression: { isActive: true } }
);
// ✅ Recommendations 5:Unique indexes ensure data integrity
db.users.createIndex({ email: 1 }, { unique: true });
(2) Antipadrões
Antipadrões comuns e suas consequências:
| Anti-padrão | Consequências | Melhores práticas |
|---|---|---|
| Campos de indexação com baixa seletividade | Baixa eficiência do índice; o otimizador pode optar por não utilizá-lo | Use um índice parcial ou um índice composto |
| Número excessivo de índices (>10 por coleção) | Queda acentuada no desempenho de gravação | Manter apenas os índices para consultas de alta frequência |
| Ordem incorreta dos campos do índice composto | Falha parcial do índice | Conformidade com as regras do ESR |
| Índices desnecessários | Espaço desperdiçado + sobrecarga de gravação | Faça uma limpeza regularmente usando $indexStats |
| Acesso a índice não verificado | O índice pode nem ter sido utilizado | É necessário executar EXPLAIN para verificar após a criação |
// ❌ Anti-pattern 1:Indexing Fields with Low Selectivity
db.users.createIndex({ isActive: 1 });
// isActive Only true/false Two values,Low index efficiency
// ❌ Anti-pattern 2:Excessive Indexing
// A set containing more than 20 Index,Write performance has dropped significantly
// ❌ Anti-pattern 3:Incorrect Order of Fields in a Composite Index
db.products.createIndex({ price: 1, category: 1 });
// Wrong: price should be placed after category
// ❌ Anti-pattern 4:Unnecessary Indexes
db.users.createIndex({ lastLoginAt: 1 });
// If you rarely press lastLoginAt Search,Index Waste
8. Monitoramento do desempenho do índice
Explicação do conceito: O monitoramento de índices é uma parte essencial das operações em um ambiente de produção. Ao analisar os registros de consultas lentas e as estatísticas de uso de índices, é possível identificar índices não utilizados (que desperdiçam espaço), índices ineficientes (que precisam de otimização) e índices ausentes (que precisam ser criados).
Como funciona: O Profiler integrado ao MongoDB registra as consultas lentas, enquanto o pipeline de agregação $indexStats monitora o número de vezes que cada índice é utilizado e o tempo gasto em cada consulta. Juntos, esses recursos oferecem uma visão abrangente do estado dos índices.
Métricas de monitoramento:
| Métrica | Comando | Valor normal | Valor de aviso |
|---|---|---|---|
| Uso do índice | $indexStats |
operações > 1.000/dia | operações = 0 (não utilizado) |
| Número de consultas lentas | system.profile |
0 | > 10/hora |
| Tamanho do índice | totalIndexSize() |
< 50% dos dados | > 100% dos dados |
| Fragmentação do índice | collStats |
Taxa média de preenchimento > 80% | < 60% |
// === Slow Query Analysis ===
db.setProfilingLevel(2, { slowms: 100 });
// Records exceeding 100ms Queries
// === View Slow Queries ===
db.system.profile.find({ millis: { $gt: 100 } })
.sort({ ts: -1 })
.limit(10);
// === View Index Usage Statistics ===
db.products.aggregate([
{ $indexStats: {} }
]);
// Find unused index
// === Delete Unused Indexes ===
db.products.dropIndex('unused_index_name');
Análise dos pontos-chave:
- Nível de perfilagem 0 = Desativado, 1 = Apenas consultas lentas, 2 = Todos os registros (o nível 2 afeta o desempenho e destina-se exclusivamente à depuração)
- Se
accesses.opspara$indexStatsfor 0, isso significa que nunca foi usado desde que o MongoDB foi iniciado. - Ambiente de produção recomendado: Nível 1 + slowms: 100, para equilibrar a granularidade do monitoramento e o desempenho
▶ Exemplo: Índice TTL + Índice Parcial + ESR na prática
// Scene 1:TTL Automatically Clear Session Indexes(30 Expires in minutes)
db.sessions.insertMany([
{ userId: 'user_001', token: 'abc', createdAt: new Date() },
{ userId: 'user_002', token: 'xyz', createdAt: new Date(Date.now() - 31 * 60 * 1000) } // Expired
]);
db.sessions.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 30 * 60 } // 30 minutes
);
// Waiting 60 seconds later,Expired documents are automatically deleted
// db.sessions.find() → Only user_001 The conversation
// Scene 2:Selected Indexes - Index only products currently for sale(Savings 70% Space)
db.products.createIndex(
{ category: 1, price: 1 },
{
partialFilterExpression: {
isActive: true,
stock: { $gt: 0 }
}
}
);
// Some indexes are only used when the query contains filter Use when conditions apply
db.products.find({
category: 'Electronics',
isActive: true, // Must match partialFilterExpression
stock: { $gt: 0 }, // Must match partialFilterExpression
price: { $gte: 100, $lte: 500 }
}).explain();
// winningPlan.inputStage.stage: IXSCAN(Partial indexing was used)
// Scene 3:ESR Rules in Practice - Order Inquiry
db.orders.insertMany([
{ userId: 'user_001', status: 'paid', createdAt: new Date('2026-07-01'), total: 100 },
{ userId: 'user_001', status: 'paid', createdAt: new Date('2026-07-02'), total: 200 },
{ userId: 'user_002', status: 'pending', createdAt: new Date('2026-07-03'), total: 50 }
]);
// ESR Best Index:Equality → Sort → Range
db.orders.createIndex({
userId: 1, // E - Equivalence Filtering
status: 1, // E - Equivalence Filtering
createdAt: -1, // S - Sort(Directional Matching)
total: 1 // R - Range Query(Save it for last)
});
// Efficient Queries:Orders Paid by the User,In reverse chronological order,Price Range
db.orders.find({
userId: 'user_001', // E
status: 'paid', // E
total: { $gte: 50, $lte: 300 } // R
}).sort({ createdAt: -1 }).limit(20) // S
.explain('executionStats');
// Output:Use composite indexes exclusively,No additional sorting required
// totalKeysExamined: 2, totalDocsExamined: 2, nReturned: 2
Resultado: O TTL limpa automaticamente as sessões expiradas; alguns índices economizam espaço; os índices compostos do ESR otimizam o desempenho das consultas.
❓ Perguntas Frequentes
P: Qual é o tempo de atraso para a exclusão de documentos do índice TTL? R: A thread em segundo plano verifica o índice uma vez a cada 60 segundos. O atraso máximo é de 60 segundos mais o tempo de vida do documento.
P: Índices parciais x índices esparsos? R: Os índices parciais suportam condições mais complexas (
$gt/$gte/$and), enquanto os índices esparsos se baseiam exclusivamente na existência ou não de um campo. Recomenda-se o uso de índices parciais (pois são mais flexíveis).
P: Qual é a direção do campo de classificação nas regras do ESR? R: A direção da classificação deve corresponder à direção do índice.
sort({ createdAt: -1 })utiliza o índicecreatedAt: -1.
P: Quando os índices são excluídos? R: (1) Manualmente
dropIndex; (2) Exclusão da coleção; (3) Expiração automática do índice por TTL (apenas campos de data).
📖 Resumo
- Índice exclusivo: garante que os valores dos campos sejam únicos
- Índice esparso: ignora campos nulos
- Índice TTL: expiração e exclusão automáticas
- Indexação parcial: Somente os documentos que atendem aos critérios são indexados
- Regra ESR: Igualdade → Ordenação → Intervalo
- Projeto de índices: Crie índices para campos com consultas de alta frequência; tenha cuidado ao criar índices para campos de baixa seletividade; e certifique-se de que os índices compostos sigam o princípio ESR.
📝 Exercícios
- Questão básica (⭐): Crie um índice único na coluna
emailda coleçãousers. - Questão básica (⭐): Crie um índice TTL para a coleção
sessions(que expira em 30 minutos). - Exercício avançado (⭐⭐): Crie um índice parcial para a coleção
products(apenas para os produtos atualmente à venda). - Problema avançado (⭐⭐): Projete um índice ótimo para a coleção
ordersutilizando a regra ESR. - Desafio (⭐⭐⭐): Projete um esquema completo de indexação para comércio eletrônico (mais de 10 índices), analise o uso com o $indexStats e exclua os índices não utilizados.