MongoDB: Fundamentos e princípios da indexação

Última atualização: 2026-08-26

Os índices são fundamentais para o desempenho do banco de dados — dominar os princípios e a otimização dos índices pode aumentar a velocidade das consultas em 1.000 vezes.

1. O que você vai aprender


2. Princípios de indexação (árvore B)

Explicação do conceito: Um índice é uma estrutura de dados auxiliar, semelhante ao índice de um livro, que permite que um banco de dados localize rapidamente um documento sem precisar vasculhar toda a coleção. O MongoDB utiliza uma estrutura de índice baseada em árvore B (o mecanismo WiredTiger usa uma variante da árvore B+) para estabelecer um mapeamento ordenado entre os valores dos campos e as localizações dos documentos, reduzindo a complexidade temporal das consultas de O(N) para O(log N).

Como funciona: Os índices B+Tree do MongoDB armazenam os valores dos campos nos nós internos para roteamento e armazenam os valores reais das chaves e os ponteiros dos documentos nos nós-folha. Os nós-folha são conectados por meio de uma lista duplamente encadeada, que suporta naturalmente consultas por intervalo e ordenação. Quando uma consulta corresponde a um índice, o mecanismo compara os valores das chaves camada por camada, começando pelo nó raiz, localizando, por fim, a posição física do documento alvo em um nó folha, evitando assim uma varredura completa da coleção (COLLSCAN).

Casos de uso:

100%
graph TB
    A[Root Node<br/>50-100] --> B[Internal Node 1<br/>20-50]
    A --> C[Internal Node 2<br/>20-50]
    B --> D[Leaf Node 1<br/>Link to the document]
    B --> E[Leaf Node 2]
    C --> F[Leaf Node 3]
    C --> G[Leaf Node 4]
    D <--> E <--> F <--> G

    style A fill:#cce5ff
    style D fill:#d4edda
    style E fill:#d4edda
    style F fill:#d4edda
    style G fill:#d4edda

Processo de consulta na árvore B+: Para uma consulta de igualdade, as comparações são feitas camada por camada, começando pelo nó raiz → até se chegar a um nó folha → e, então, o ponteiro do documento é retornado; para uma consulta de intervalo, após localizar o nó folha inicial, a lista encadeada é percorrida sequencialmente → e todos os documentos que atendem aos critérios são coletados.

100%
sequenceDiagram
    participant App as Application Inquiry
    participant WT as WiredTigerEngine
    participant IX as B+TreeIndex
    participant DOC as Collection of Documents

    App->>WT: find({sku: 'SKU-005'})
    WT->>IX: Root Node Comparison 50<005<100 → Zuo Zishu
    IX-->>WT: Internal Node: 20<005<50 → Left lobe
    WT->>IX: Leaf Node Search SKU-005
    IX-->>WT: Found → doc_pointer=0x7F3A
    WT->>DOC: Read 0x7F3A Location Documentation
    DOC-->>App: Return matching documents

    Note over WT,IX: Time Complexity O(log N)<br/>No need to scan all the documents
Operação Varredura completa da tabela (COLLSCAN) Índice B+Tree (IXSCAN) Diferença de desempenho
Consulta de valores iguais O(N) O(log N) 100.000 linhas: 100 mil vs 17
Consulta de intervalo O(N) O(log N + K) 100.000 linhas: 100 mil vs 17 mil+
Classificação O(N log N) O(log N + K) O índice já está naturalmente classificado, portanto não é necessária nenhuma classificação
Inserir/Atualizar O(1) O(log N) Sobrecarga adicional para manutenção do índice

(1) Detalhes sobre o armazenamento de índices do WiredTiger

Dimensão Descrição
Formato do índice Árvore B+, pares chave-valor armazenados em ordem classificada
Nó folha Contém a chave de índice + RecordID (ponteiro de localização do documento)
Nó interno Contém apenas a chave da rota e um ponteiro para um nó filho
Conexão em lista encadeada Os nós-folha formam uma lista duplamente encadeada, permitindo a varredura sequencial
Compressão A compressão por prefixo reduz o espaço de armazenamento

3. Sintaxe do createIndex

Explicação do conceito: createIndex() é o comando principal para criar índices no MongoDB; ele instrui o mecanismo a construir uma estrutura de índice B+Tree para o campo especificado. Uma vez criado o índice, o otimizador de consultas determina automaticamente se deve utilizá-lo — os desenvolvedores não precisam modificar a instrução de consulta.

Como funciona: Ao criar um índice, o MongoDB analisa todos os documentos da coleção, extrai os valores dos campos indexados, os classifica e constrói uma estrutura B+Tree que é gravada no disco. Durante esse processo, é aplicado um bloqueio de gravação à coleção (os índices podem ser criados em segundo plano background: true), e a criação de um índice em uma coleção grande pode levar de alguns minutos a várias horas.

Regras gramaticais:

Parâmetro Tipo Descrição
keys objeto Campos de índice e ordem: 1 ascendente, -1 descendente
unique booleano Indica se é um índice exclusivo; o valor padrão é false
background booleano Indica se a compilação deve ser feita em segundo plano (sem bloquear operações de leitura/gravação); o valor padrão é false
name string Nome do índice personalizado; o padrão é field_1
partialFilterExpression objeto Critérios de indexação parcial (somente os documentos que atendem aos critérios são indexados)
sparse booleano Índice esparso; ignorar campos nulos
expireAfterSeconds número Índice TTL, expira automaticamente e é excluído (segundos)
v número Versão do índice; o padrão é v=2
JAVASCRIPT
// === Create a single-field index ===
db.products.createIndex({ sku: 1 });          // Ascending
db.products.createIndex({ createdAt: -1 });   // Descending

// === Create a composite index ===
db.products.createIndex({ category: 1, price: -1 });

// === Create a unique index ===
db.products.createIndex({ sku: 1 }, { unique: true });

// === Create in the Backend(Non-blocking)===
db.products.createIndex({ tags: 1 }, { background: true });

// === Custom Index Name ===
db.products.createIndex({ title: 1 }, { name: 'idx_title' });

Análise dos pontos-chave:

  1. Os valores 1 e -1 afetam a ordem de classificação dos índices; eles não têm impacto prático nos índices de campo único, mas são cruciais para a otimização da classificação em índices compostos.
  2. background: true No MongoDB 4.2 e versões posteriores, isso é executado por padrão em segundo plano; os parâmetros são mantidos, mas não precisam mais ser especificados explicitamente.
  3. Cada coleção possui um índice único padrão chamado _id (que não pode ser excluído); não há necessidade de criar outro.

▶ Exemplo 1: createIndex e monitoramento da criação de índices

JAVASCRIPT
// ShopHub E-commerce:Create a key index for the product collection,Monitoring Build Progress
db.products.createIndex({ category: 1, price: -1, rating: -1 }, { name: 'idx_category_price_rating', background: true });

// View Index Build Progress
db.currentOp({
  $or: [
    { op: 'command', 'command.createIndexes': { $exists: true } },
    { op: 'none', ns: /shopdb\.products/ }
  ]
});

// View All Indexes
db.products.getIndexes();
// [
//   { v: 2, key: { _id: 1 }, name: '_id_' },
//   { v: 2, key: { category: 1, price: -1, rating: -1 }, name: 'idx_category_price_rating' }
// ]

4. Plano de execução do explain()

Explicação do conceito: explain() é uma ferramenta de análise de consultas para o MongoDB que retorna o plano de execução selecionado pelo otimizador de consultas, incluindo métricas-chave, como se um índice foi utilizado, quantos documentos foram examinados e quanto tempo a operação levou. Ela funciona como um “raio-X” para a otimização de índices — execute explain() primeiro e, em seguida, otimize.

Como funciona: O otimizador de consultas do MongoDB gera vários planos candidatos para cada consulta, os executa, compara seu desempenho e armazena em cache o plano ideal. explain() Apresenta três níveis:

100%
graph LR
    A[Query Request] --> B[Query Optimizer]
    B --> C[Generate Candidate Plans]
    C --> D[Plan A: IXSCAN]
    C --> E[Plan B: COLLSCAN]
    D --> F[Execution Comparison]
    E --> F
    F --> G[Selecting the Optimal Plan]
    G --> H[Cache + Execute]

    style G fill:#d4edda
    style E fill:#f8d7da

Casos de uso:

JAVASCRIPT
// === View the query execution plan ===
db.products.find({ category: 'Electronics' }).explain('executionStats');

// === Key Metrics ===
{
  queryPlanner: {
    winningPlan: {
      stage: 'IXSCAN',           // Index Scan(✅)
      // stage: 'COLLSCAN',      // Exhaustive Scan(❌)
      inputStage: {
        stage: 'IXSCAN',
        indexName: 'category_1'
      }
    }
  },
  executionStats: {
    totalDocsExamined: 250,      // Number of scanned documents
    totalKeysExamined: 250,      // Number of Scan Index Keys
    nReturned: 250,              // Number of documents returned
    executionTimeMillis: 5,      // Execution Time(milliseconds)
    totalQueryPlanExecutionTime: 7
  }
}

(1) Interpretação dos principais indicadores

Indicador Valor-alvo Problema Descrição
stage IXSCAN COLLSCAN (varredura completa da tabela) O COLLSCAN requer otimização de índice
totalDocsExamined ≈ nReturned Muito maior que nReturned Um valor mais de 10 vezes maior indica baixa precisão do índice
totalKeysExamined ≈ nReturned Muito maior que nReturned O intervalo da varredura do índice é muito grande
executionTimeMillis < 50 ms > 100 ms Considere a cobertura do índice ou índices compostos
indexName Nome do índice de destino id ou nenhum Confirme se o índice esperado foi encontrado

Comparação dos três modos de explicação:

Modo Saída Cenários aplicáveis
'queryPlanner' Apenas planejamento, sem execução Verifique rapidamente se o índice está sendo usado
'executionStats' Estatísticas de planejamento e execução Análise de desempenho (mais comum)
'allPlansExecution' Estatísticas de todos os planos candidatos Análise da seleção do otimizador

▶ Exemplo 2: Usando explain() para diagnosticar consultas lentas

JAVASCRIPT
// TechCorp: The system has detected that order lookups are becoming slower. Use explain to diagnose
// Before the Index:COLLSCAN
db.orders.find({ status: 'paid', total: { $gte: 100 } }).explain('executionStats');
// stage: 'COLLSCAN', totalDocsExamined: 100000, executionTimeMillis: 450

// Create a composite index
db.orders.createIndex({ status: 1, total: -1 });

// After indexing:IXSCAN
db.orders.find({ status: 'paid', total: { $gte: 100 } }).explain('executionStats');
// stage: 'IXSCAN', indexName: 'status_1_total_-1'
// totalDocsExamined: 5000, nReturned: 5000, executionTimeMillis: 8

// Efficiency Assessment: examined/returned = 1.0 (Optimal Value), Performance improved 56x

5. Índices compostos e o prefixo mais à esquerda

Explicação do conceito: Um índice composto é um único índice criado a partir de vários campos (por exemplo, { category: 1, price: -1, rating: 1 }). Ele é mais eficiente do que vários índices de campo único, pois uma única consulta ao índice pode atender a várias condições de consulta simultaneamente. O princípio do “prefixo mais à esquerda” é a regra fundamental dos índices compostos — o índice só é eficaz quando os campos são utilizados consecutivamente, começando pelo campo mais à esquerda.

Como funciona: Um índice composto constrói uma árvore B+ com base na ordem dos campos. Primeiro, ele classifica pelo primeiro campo; se os valores do primeiro campo forem iguais, ele classifica pelo segundo campo e assim por diante. Durante uma consulta, a correspondência deve começar pelo campo mais à esquerda; pular quaisquer campos prefixais tornará os campos subsequentes no índice ineficazes — assim como você precisa primeiro determinar a primeira letra ao procurar uma palavra em um dicionário.

100%
graph TB
    subgraph "Composite Index {category, price, rating}"
        A[Electronics<br/>$100<br/>★5] --> B[Electronics<br/>$200<br/>★4]
        B --> C[Electronics<br/>$300<br/>★3]
        C --> D[Books<br/>$10<br/>★5]
        D --> E[Books<br/>$20<br/>★4]
    end

    subgraph "Query Hit Analysis"
        F["✅ {category}"] --> G["✅ {category, price}"]
        G --> H["✅ {category, price, rating}"]
        I["❌ {price}"] --> J["Skip category"]
        K["❌ {rating}"] --> L["Skip category, price"]
        M["❌ {category, rating}"] --> N["Skip price"]
    end

    style F fill:#d4edda
    style G fill:#d4edda
    style H fill:#d4edda
    style I fill:#f8d7da
    style K fill:#f8d7da
    style M fill:#f8d7da

Casos de uso:

Padrão de consulta Corresponde a {categoria, preço, avaliação} Motivo
{category: 'A'} ✅ Todas as correspondências Use o prefixo “categoria”
{category: 'A', price: {$gte: 100}} ✅ Todos os resultados Use o prefixo “categoria + preço”
{category: 'A', price: {$gte: 100}, rating: 5} ✅ Todas as correspondências Correspondência exata em três campos
{price: {$gte: 100}} ❌ Nenhuma correspondência Ignorar a categoria mais à esquerda
{rating: 5} ❌ Nenhuma correspondência Ignorar categoria, preço
{category: 'A', rating: 5} ⚠️ Apenas “categoria” e “preço” estão com problemas; “avaliação” não está disponível
JAVASCRIPT
// === Composite Index:{ category: 1, price: -1, rating: 1 } ===
db.products.createIndex({ category: 1, price: -1, rating: 1 });

// ✅ Queries Using Indexes:
db.products.find({ category: 'Electronics' });                                    // Uses category
db.products.find({ category: 'Electronics', price: { $gte: 100 } });            // Uses category + price
db.products.find({ category: 'Electronics', price: { $gte: 100 }, rating: 5 }); // Use all 3 Field

// ⚠️ Queries That Do Not Use Indexes:
db.products.find({ price: { $gte: 100 } });                                      // Skip category
db.products.find({ rating: 5 });                                                 // Skip category, price
db.products.find({ category: 'Electronics', rating: 5 });                       // Skip price

Princípio do prefixo mais à esquerda: Os índices compostos só são eficazes quando usados consecutivamente, começando pelo campo mais à esquerda. Pular campos intermediários rompe a cadeia do índice — os campos subsequentes não podem utilizar o índice.

Regra ESR (Igualdade → Ordenação → Intervalo): A regra de ouro para a ordem dos campos em um índice composto — os campos de filtro de igualdade vêm primeiro, os campos de ordenação no meio e os campos de consulta de intervalo por último. Esse assunto será abordado em profundidade em uma lição posterior (Lição 19).


6. Cobertura do índice

Explicação do conceito: Uma consulta coberta é aquela em que todos os campos necessários para a consulta estão incluídos no índice, permitindo que o mecanismo retorne resultados diretamente do índice, sem a necessidade de buscar o documento original. Essa é a forma definitiva de otimização do índice — a consulta não requer nenhum acesso aos dados do documento.

Como funciona: O processo padrão de consulta é “varredura do índice → recuperação do ponteiro do documento → acesso ao documento na tabela → extração dos campos → retorno”; o processo de consulta baseado em índice é “varredura do índice → extração dos campos diretamente do índice → retorno”. Ao eliminar a necessidade de acessar a tabela, as operações de E/S são reduzidas pela metade e o desempenho melhora em mais de 50%.

100%
graph LR
    subgraph "General Inquiry"
        A1[Index Scan] --> A2[Get doc pointer]
        A2 --> A3[Return to the table and read the document]
        A3 --> A4[Extract Fields]
        A4 --> A5[Return Results]
    end

    subgraph "Index-Covered Queries"
        B1[Index Scan] --> B2[Directly Extract Index Fields]
        B2 --> B3[Return Results]
    end

    style A3 fill:#f8d7da
    style B2 fill:#d4edda

Casos de uso:

Dimensão Consulta normal Cobertura do índice
Fluxo de execução Varredura de índice → Pesquisar documento na tabela Varredura de índice → Retornar diretamente
Número de operações de E/S 2 (índice + documento) 1 (apenas índice)
Desempenho Teste de desempenho 50% mais rápido
explicar indicador FETCH estágio PROJECTION_COVERED
Restrições Nenhuma A projeção deve excluir _id
JAVASCRIPT
// === General Inquiry:I need to go back to the table to check the documentation. ===
db.products.find(
  { category: 'Electronics' },
  { sku: 1, title: 1, price: 1 }
);

// === Index-Covered Queries:Returned directly from the index ===
db.products.createIndex({ category: 1, sku: 1, title: 1, price: 1 });

db.products.find(
  { category: 'Electronics' },
  { sku: 1, title: 1, price: 1, _id: 0 }  // _id: 0 Must be excluded
);
// explain Displayed in the middle stage: 'PROJECTION_COVERED' ✅

Análise dos pontos-chave:

  1. _id é sempre retornado por padrão e não está incluído no índice regular; é necessário usar _id: 0 para excluí-lo, a fim de substituí-lo.
  2. Quanto mais campos de índice houver, mais consultas eles abrangem, mas maior fica o tamanho do índice — é preciso encontrar um equilíbrio.
  3. A cobertura por índice é mais eficaz para documentos grandes (a redução na quantidade de E/S é proporcional ao tamanho do documento).

7. Gerenciamento de índices

Explicação do conceito: O gerenciamento de índices inclui a criação, visualização, exclusão e reconstrução de índices. Em um ambiente de produção, os índices não são algo que se possa simplesmente “configurar e esquecer”; é necessário monitorar continuamente seu uso, excluir índices não utilizados e reconstruir índices fragmentados.

Ciclo de vida do índice:

100%
graph LR
    A[Analyzing Query Patterns] --> B[Design Index]
    B --> C[Create an Index<br/>background:true]
    C --> D[Validate Hit<br/>explain()]
    D --> E[Monitoring Utilization<br/>$indexStats]
    E --> F{Low utilization rate?}
    F -->|Yes| G[Delete Index]
    F -->|No| H[Continue monitoring]
    G --> A
    H --> E

    style G fill:#f8d7da
    style D fill:#d4edda
Operações Administrativas Comando Descrição
Ver Índice db.col.getIndexes() Listar todos os índices e definições de chaves
Excluir índice db.col.dropIndex(name) Excluir por nome ou definição de chave
Excluir tudo db.col.dropIndexes() Excluir todos os índices (manter _id)
Recriar índice db.col.reIndex() Recriar todos os índices (desfragmentar)
Tamanho do índice db.col.totalIndexSize() Visualizar o uso de espaço do índice (bytes)
Uso do índice db.col.aggregate([{$indexStats:{}}]) Visualizar o número de vezes que cada índice foi utilizado
JAVASCRIPT
// === View all indexes in the collection ===
db.products.getIndexes();

// === Delete Index ===
db.products.dropIndex('sku_1');
db.products.dropIndex({ sku: 1 });

// === Delete all indexes(Retain _id)===
db.products.dropIndexes();

// === Rebuild Index ===
db.products.reIndex();

// === View Index Size ===
db.products.totalIndexSize();

Análise dos pontos-chave:

  1. reIndex() Bloqueia a coleção; em um ambiente de produção, recomenda-se executar isso durante uma janela de manutenção.
  2. dropIndex() Recomendamos usar primeiro o $indexStats para confirmar que o índice realmente não está em uso.
  3. Recomenda-se que cada índice definido não contenha mais do que 10 entradas; um número excessivo de entradas afetará o desempenho de gravação.

8. Custo do índice

Explicação do conceito: Os índices não são gratuitos — cada índice ocupa espaço em disco, aumenta a sobrecarga de gravação e consome memória. Compreender o custo dos índices é fundamental para fazer as escolhas certas. “Quanto mais índices, melhor” é o equívoco mais comum.

Como funciona: Sempre que um documento é inserido, atualizado ou excluído, o MongoDB precisa atualizar de forma síncrona as estruturas B+Tree de todos os índices relevantes. Se uma coleção tiver N índices, as operações de gravação exigem a manutenção de N B+Trees. Quanto mais índices houver, mais lentas se tornam as gravações e maior é o uso de memória (já que o cache do WiredTiger precisa carregar as páginas dos índices).

100%
graph LR
    A[Create an index] --> B[Faster reads]
    A --> C[Slower writes]
    A --> D[Takes up space]

    B --> B1[Search +1000x]
    C --> C1[Insert/Update +50% Expenses]
    D --> D1[Per Index 1-10 MB]

    subgraph "Weighing Decisions"
        E[High query frequency?] -->|Yes| F[✅ Create an index]
        E -->|No| G[❌ Do not build]
        H[High write frequency?] -->|Yes| I[⚠️ Caution]
        H -->|No| F
    end

Quantificando o custo da indexação:

Dimensão de custo Impacto Quantificação
Espaço em disco Cada índice ocupa aproximadamente 5–20% do volume de dados 10 GB de dados × 8 índices ≈ 4–16 GB de espaço adicional
Latência de gravação Cada índice adicional aumenta o tempo de gravação em cerca de 5–10% 8 índices → As gravações ficam 40–80% mais lentas
Uso de memória O cache do WiredTiger exige o carregamento de páginas de índice Deterioração do desempenho das consultas quando o índice não está em cache
Custos de manutenção Operações como reindexação, monitoramento e reconstrução Quanto mais índices houver, mais complexa se torna a manutenção

Compromissos relacionados a índices:

Estratégia de seleção de índices:

Cenário Recomendação Motivo
Pesquisa de produtos no comércio eletrônico Índice composto de categoria + preço Filtragem e classificação de alta frequência
Login do usuário Índice único de e-mail Equi-join + restrição de unicidade
Consulta de log Índice de TTL de createdAt Intervalo de tempo + expiração automática
Campo de status (baixa seletividade) Não é recomendável criá-lo separadamente isActive tem apenas 2 valores, portanto a eficiência da indexação é extremamente baixa
Pesquisa de texto Índice de texto Apenas para pesquisa de texto completo

9. Treinamento prático abrangente

(1) Princípios de projeto de índices

Explicação detalhada da regra ESR: A ordem dos campos em um índice composto deve seguir a sequência Equalidade → Sorteio → Range. Coloque o campo de filtro de igualdade em primeiro lugar (para restringir rapidamente o intervalo), o campo de ordenação no meio (para aproveitar a ordem de classificação do índice e evitar a ordenação na memória) e o campo de consulta de intervalo por último (já que uma varredura de intervalo interromperá o uso dos campos de índice subsequentes).

100%
graph LR
    E["Equality<br/>Equivalence Filtering<br/>category='A'"] --> S["Sort<br/>Sort<br/>createdAt: -1"]
    S --> R["Range<br/>Scope<br/>price >= 100"]

    style E fill:#d4edda
    style S fill:#cce5ff
    style R fill:#fff3cd
Ordem do índice Eficiência da consulta Motivo
{E, S, R} ⭐⭐⭐ Ótimo Correspondência exata → Classificação por índice → Varredura de intervalo
{E, R, S} ⭐⭐ Bom Posicionamento por equivalência → Varredura de intervalo → Classificação por memória
{R, S, E} ⭐ Ruim A varredura de intervalo é muito ampla, o que resulta na perda da vantagem dos isovalores
JAVASCRIPT
// === Design of an E-commerce Product Index ===
db.products.createIndex({ sku: 1 }, { unique: true });                    // Unique Index
db.products.createIndex({ category: 1, price: -1 });                       // Category Page
db.products.createIndex({ category: 1, rating: -1 });                      // Categories+Rating
db.products.createIndex({ isActive: 1, createdAt: -1 });                   // Listing Date
db.products.createIndex({ title: 'text', description: 'text' });          // Full-Text Search
db.products.createIndex({ tags: 1 });                                      // Tag Filtering

(2) Análise do uso de índices

JAVASCRIPT
// === Analyzing Slow Queries ===
db.products.find({
  category: 'Electronics',
  price: { $gte: 100, $lte: 1000 },
  isActive: true
}).sort({ createdAt: -1 }).limit(20);

// Check whether an index is being used
const explain = db.products.find({...}).explain('executionStats');
print('Stage:', explain.queryPlanner.winningPlan.stage);
print('Docs Examined:', explain.executionStats.totalDocsExamined);
print('Time:', explain.executionStats.executionTimeMillis, 'ms');

▶ Exemplo: Projeto prático de índices e análise de desempenho

JAVASCRIPT
// 1. Create a Test Set(10 10,000 product records)
for (let i = 0; i < 100000; i++) {
  db.products.insertOne({
    sku: 'SKU-' + i.toString().padStart(6, '0'),
    title: 'Product ' + i,
    category: ['Electronics', 'Books', 'Clothing', 'Home'][i % 4],
    price: Math.random() * 1000,
    stock: Math.floor(Math.random() * 100),
    createdAt: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000),
    isActive: true
  });
}

// 2. Create an Index
db.products.createIndex({ sku: 1 }, { unique: true });
db.products.createIndex({ category: 1, price: -1 });  // Composite Index
db.products.createIndex({ createdAt: -1 });

// 3. Comparison:Indexed vs No index
console.time('Unindexed Query');
db.products.find({ category: 'Electronics', price: { $gte: 100, $lte: 500 } }).toArray();
console.timeEnd('Unindexed Query');  // ~500ms

// After creating the index
db.products.createIndex({ category: 1, price: 1 });

console.time('Indexed queries');
db.products.find({ category: 'Electronics', price: { $gte: 100, $lte: 500 } }).toArray();
console.timeEnd('Indexed queries');  // ~5ms(Performance ↑100x)

// 4. explain() Analyze the Execution Plan
const explain = db.products.find({
  category: 'Electronics',
  price: { $gte: 100, $lte: 500 }
}).sort({ createdAt: -1 }).limit(20).explain('executionStats');

print('Stage:', explain.queryPlanner.winningPlan.stage);  // IXSCAN
print('Index:', explain.queryPlanner.winningPlan.inputStage?.indexName);  // category_1_price_1
print('Docs Examined:', explain.executionStats.totalDocsExamined);
print('Keys Examined:', explain.executionStats.totalKeysExamined);
print('Returned:', explain.executionStats.nReturned);
print('Time:', explain.executionStats.executionTimeMillis, 'ms');

// 5. Index-Covered Queries(No need to return to the table)
db.products.createIndex({ category: 1, sku: 1, price: 1 });

db.products.find(
  { category: 'Electronics' },
  { sku: 1, price: 1, _id: 0 }  // Return only fields that are already in the index
).explain();

// Stage: PROJECTION_COVERED(No need to consult the documentation)

Resultado: O índice melhora o desempenho da consulta em 100 vezes; EXPLAIN() mostra IXSCAN (varredura de índice) + PROJECTION_COVERED (cobertura de índice).

❓ Perguntas Frequentes

P: É melhor ter mais índices? R: Não. Os índices aceleram as leituras, mas tornam as gravações mais lentas e ocupam espaço. Geralmente, 5 a 10 por coleção é o número adequado.

P: A ordem dos campos em um índice composto é importante? R: É muito importante. Coloque os campos com alta seletividade (muitos valores únicos) no início, seguindo o princípio ESR (Igualdade → Classificação → Intervalo).

P: Por que o índice não está funcionando? R: Causas comuns: (1) A ordem dos campos não corresponde ao prefixo mais à esquerda; (2) Foi usado $ne, $nin ou $exists; (3) Os tipos de dados não correspondem; (4) A coleção contém poucos registros (< 100 registros não são otimizados).

P: Como é possível otimizar o COLLSCAN no explain()? R: Analise as condições da consulta e crie índices nas colunas de filtragem. Considere o uso de índices compostos que abranjam todas as condições de filtragem.


📖 Resumo


📝 Exercícios

  1. Questão básica (⭐): Crie três índices — sku, category e createdAt — para a coleção de produtos.
  2. Pergunta básica (⭐): Use explain() para analisar uma consulta e confirmar se um índice está sendo utilizado (IXSCAN).
  3. Exercício avançado (⭐⭐): Crie um índice composto { categoria: 1, preço: -1 } para testar o princípio do prefixo mais à esquerda (que consultas utilizam o índice).
  4. Problema avançado (⭐⭐): Implemente uma consulta coberta por índice (em que todos os campos estejam incluídos no índice).
  5. Questão de desafio (⭐⭐⭐): Elabore um esquema completo de indexação para produtos de comércio eletrônico (8 índices) e analise os cenários de consulta para cada índice.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%