Use Índices do MongoDB

MongoDBBeginner
Pratique Agora

Introdução

Neste laboratório, você aprenderá os fundamentos do uso de índices do MongoDB para otimizar o desempenho de consultas. Um índice é uma estrutura de dados especial que contém uma porção pequena e fácil de pesquisar dos dados de uma coleção, permitindo que o MongoDB encontre documentos muito mais rapidamente do que escaneando a coleção inteira.

Você começará observando o desempenho de uma consulta sem índice. Em seguida, criará índices de campo único e compostos e verá como eles melhoram drasticamente as velocidades de consulta e ordenação. Finalmente, aprenderá a gerenciar seus índices listando-os e removendo-os. Ao final deste laboratório, você terá uma compreensão prática de como criar e usar índices para tornar suas aplicações MongoDB mais eficientes.

Este é um Lab Guiado, que fornece instruções passo a passo para ajudá-lo a aprender e praticar. Siga as instruções cuidadosamente para completar cada etapa e ganhar experiência prática. Dados históricos mostram que este é um laboratório de nível iniciante com uma taxa de conclusão de 100%. Recebeu uma taxa de avaliações positivas de 100% dos estudantes.

Consultando Sem um Índice

Antes de criar um índice, é importante entender como o MongoDB funciona sem um. Nesta etapa, você configurará uma coleção de exemplo, executará uma consulta e analisará seu plano de execução para ver o impacto no desempenho de uma varredura completa da coleção (collection scan).

Primeiro, abra o MongoDB Shell (mongosh) para interagir com seu banco de dados. Esta interface de linha de comando permite executar comandos diretamente em sua instância do MongoDB.

mongosh

Uma vez dentro do shell, você verá o prompt >. Vamos mudar para um novo banco de dados chamado indexlab e inserir alguns documentos de exemplo em uma coleção users. Se o banco de dados ou a coleção não existirem, o MongoDB os criará automaticamente.

use indexlab
db.users.insertMany([
  { name: "Alice", age: 28, city: "New York" },
  { name: "Bob", age: 35, city: "San Francisco" },
  { name: "Charlie", age: 42, city: "Chicago" },
  { name: "David", age: 25, city: "New York" },
  { name: "Eve", age: 31, city: "San Francisco" }
]);

Agora, vamos encontrar todos os usuários com mais de 30 anos. Usaremos o método .explain("executionStats") para ver como o MongoDB executa esta consulta. Este método fornece estatísticas detalhadas sobre o plano de execução da consulta.

db.users.find({ age: { $gt: 30 } }).explain("executionStats");

A saída fornece estatísticas detalhadas sobre a execução da consulta. Procure pelas seções winningPlan e executionStats.

Exemplo de saída (truncada):

{
  "queryPlanner": {
    "winningPlan": {
      "stage": "COLLSCAN",
      "filter": { "age": { "$gt": 30 } },
      "direction": "forward"
    }
  },
  "executionStats": {
    "executionSuccess": true,
    "nReturned": 3,
    "executionTimeMillis": 0,
    "totalKeysExamined": 0,
    "totalDocsExamined": 5
  }
}

As informações chave aqui são stage: "COLLSCAN" e totalDocsExamined: 5.

  • COLLSCAN significa "Collection Scan" (Varredura de Coleção). Isso significa que o MongoDB teve que inspecionar cada documento na coleção para encontrar aqueles que correspondem à consulta.
  • totalDocsExamined: 5 confirma que todos os 5 documentos na coleção foram escaneados.

Embora isso seja rápido para uma coleção pequena, uma varredura de coleção em milhões de documentos seria muito lenta. Na próxima etapa, você corrigirá isso adicionando um índice.

Criando e Usando um Índice de Campo Único

Agora que você viu a ineficiência de uma varredura de coleção, vamos melhorar o desempenho criando um índice. Um índice no campo age permitirá que o MongoDB encontre rapidamente os documentos relevantes sem varrer a coleção inteira.

Você ainda deve estar no shell mongosh da etapa anterior.

Crie um índice no campo age em ordem ascendente. O 1 especifica um índice ascendente, enquanto -1 especificaria um descendente.

db.users.createIndex({ age: 1 });

O MongoDB confirmará que o índice foi criado com sucesso. O nome padrão para este índice será age_1.

Agora, execute exatamente a mesma consulta da etapa anterior e examine seu plano de execução.

db.users.find({ age: { $gt: 30 } }).explain("executionStats");

Exemplo de saída (truncada):

{
  "queryPlanner": {
    "winningPlan": {
      "stage": "FETCH",
      "inputStage": {
        "stage": "IXSCAN",
        "keyPattern": { "age": 1 },
        "indexName": "age_1"
      }
    }
  },
  "executionStats": {
    "executionSuccess": true,
    "nReturned": 3,
    "executionTimeMillis": 0,
    "totalKeysExamined": 3,
    "totalDocsExamined": 3
  }
}

Observe as mudanças significativas no plano de execução:

  • O stage agora é IXSCAN, que significa "Index Scan" (Varredura de Índice). Isso indica que o MongoDB usou o índice age_1 para encontrar os documentos correspondentes.
  • totalKeysExamined e totalDocsExamined agora são 3, e não 5. O MongoDB teve que examinar apenas os 3 documentos que correspondiam à consulta através do índice, ignorando os outros 2. Esta é a origem do ganho de desempenho.

Usando um Índice Composto para Ordenação

Índices não servem apenas para acelerar consultas; eles também são cruciais para uma ordenação eficiente. Quando você ordena por um campo que não está indexado, o MongoDB precisa realizar a ordenação na memória, o que pode ser lento e consumir RAM significativa. Um índice composto, que inclui múltiplos campos, pode otimizar consultas que filtram e ordenam por esses campos.

Vamos criar um índice composto nos campos city (ascendente) e age (descendente). A ordem dos campos no índice é importante para como ele pode ser utilizado.

db.users.createIndex({ city: 1, age: -1 });

Agora, vamos executar uma consulta que ordena os usuários por cidade e depois por idade. Usaremos .explain() novamente para confirmar que o índice está sendo usado para a ordenação.

db.users.find().sort({ city: 1, age: -1 }).explain("executionStats");

Exemplo de saída (truncada):

{
  "queryPlanner": {
    "winningPlan": {
      "stage": "FETCH",
      "inputStage": {
        "stage": "IXSCAN",
        "keyPattern": { "city": 1, "age": -1 },
        "indexName": "city_1_age_-1"
      }
    }
  }
}

O estágio IXSCAN mostra que o MongoDB usou nosso novo índice city_1_age_-1. Como os dados já estão ordenados no índice de acordo com nossos critérios de ordenação, o MongoDB não precisa realizar uma etapa de ordenação separada e custosa na memória.

Para ver o resultado ordenado real, execute a consulta sem .explain().

db.users.find().sort({ city: 1, age: -1 });

Saída:

[
  { _id: ObjectId("..."), name: 'Charlie', age: 42, city: 'Chicago' },
  { _id: ObjectId("..."), name: 'Alice', age: 28, city: 'New York' },
  { _id: ObjectId("..."), name: 'David', age: 25, city: 'New York' },
  { _id: ObjectId("..."), name: 'Bob', age: 35, city: 'San Francisco' },
  { _id: ObjectId("..."), name: 'Eve', age: 31, city: 'San Francisco' }
]

Os documentos estão corretamente ordenados primeiro por city alfabeticamente e depois por age do mais velho para o mais novo dentro de cada cidade, correspondendo à definição do índice composto.

Gerenciando e Removendo Índices

Embora os índices melhorem o desempenho de leitura, eles não são gratuitos. Eles consomem espaço de armazenamento e adicionam uma pequena quantidade de sobrecarga às operações de escrita (inserções, atualizações e exclusões). Portanto, é uma boa prática revisar e remover periodicamente os índices que não são mais utilizados.

Primeiro, você pode listar todos os índices em uma coleção usando o método getIndexes().

db.users.getIndexes();

Saída:

[
  { "v": 2, "key": { "_id": 1 }, "name": "_id_" },
  { "v": 2, "key": { "age": 1 }, "name": "age_1" },
  { "v": 2, "key": { "city": 1, "age": -1 }, "name": "city_1_age_-1" }
]

Isso mostra o índice padrão no campo _id, que é criado automaticamente para cada coleção, e os dois índices que criamos.

Digamos que determinamos que o índice composto city_1_age_-1 não é mais necessário. Você pode removê-lo usando o método dropIndex(), passando o nome do índice como argumento.

db.users.dropIndex("city_1_age_-1");

O MongoDB retornará um objeto indicando quantos índices existiam antes da operação de exclusão.

{ "nIndexesWas": 3, "ok": 1 }

Agora, verifique se o índice foi removido listando os índices novamente.

db.users.getIndexes();

Saída:

[
  { "v": 2, "key": { "_id": 1 }, "name": "_id_" },
  { "v": 2, "key": { "age": 1 }, "name": "age_1" }
]

Como você pode ver, o índice city_1_age_-1 desapareceu. O gerenciamento adequado de índices é uma parte fundamental para manter um banco de dados saudável e com bom desempenho.

Para sair do shell do MongoDB, você pode digitar exit ou pressionar Ctrl+D.

exit;

Resumo

Neste laboratório, você aprendeu as técnicas essenciais para usar índices do MongoDB. Você começou observando um COLLSCAN em uma consulta sem índice e compreendeu suas limitações de desempenho. Em seguida, criou um índice de campo único, que alterou o plano de consulta para um IXSCAN muito mais eficiente.

Além disso, você explorou índices compostos e viu como eles podem ser usados para otimizar operações de ordenação, evitando ordenações custosas na memória. Finalmente, você aprendeu a gerenciar seus índices listando-os com getIndexes() e removendo os não utilizados com dropIndex(). Essas habilidades são fundamentais para construir aplicações rápidas e escaláveis com o MongoDB.