Uso de Índices en MongoDB

MongoDBBeginner
Practicar Ahora

Introducción

En este laboratorio, aprenderá los fundamentos del uso de índices de MongoDB para optimizar el rendimiento de las consultas. Un índice es una estructura de datos especial que contiene una porción pequeña y fácil de buscar de los datos de una colección, lo que permite a MongoDB encontrar documentos mucho más rápido que escaneando la colección completa.

Comenzará observando el rendimiento de una consulta sin un índice. Luego, creará índices de campo único y compuestos y verá cómo mejoran drásticamente las velocidades de consulta y ordenación. Finalmente, aprenderá a administrar sus índices listándolos y eliminándolos. Al final de este laboratorio, tendrá una comprensión práctica de cómo crear y usar índices para hacer que sus aplicaciones de MongoDB sean más eficientes.

Este es un Guided Lab, que proporciona instrucciones paso a paso para ayudarte a aprender y practicar. Sigue las instrucciones cuidadosamente para completar cada paso y obtener experiencia práctica. Los datos históricos muestran que este es un laboratorio de nivel principiante con una tasa de finalización del 100%. Ha recibido una tasa de reseñas positivas del 100% por parte de los estudiantes.

Consultar sin Índice

Antes de crear un índice, es importante comprender cómo funciona MongoDB sin uno. En este paso, configurará una colección de ejemplo, ejecutará una consulta y analizará su plan de ejecución para ver el impacto en el rendimiento de un escaneo completo de la colección.

Primero, abra la MongoDB Shell (mongosh) para interactuar con su base de datos. Esta interfaz de línea de comandos le permite ejecutar comandos directamente contra su instancia de MongoDB.

mongosh

Una vez dentro de la shell, verá el prompt >. Cambiemos a una nueva base de datos llamada indexlab e insertemos algunos documentos de ejemplo en una colección llamada users. Si la base de datos o la colección no existen, MongoDB las creará automáticamente.

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" }
]);

Ahora, busquemos todos los usuarios mayores de 30 años. Usaremos el método .explain("executionStats") para ver cómo MongoDB ejecuta esta consulta. Este método proporciona estadísticas detalladas sobre el plan de ejecución de la consulta.

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

La salida proporciona estadísticas detalladas sobre la ejecución de la consulta. Busque las secciones winningPlan y executionStats.

Salida de ejemplo (truncada):

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

La información clave aquí es stage: "COLLSCAN" y totalDocsExamined: 5.

  • COLLSCAN significa "Collection Scan" (Escaneo de Colección). Significa que MongoDB tuvo que inspeccionar cada documento de la colección para encontrar los que coinciden con la consulta.
  • totalDocsExamined: 5 confirma que se escanearon los 5 documentos de la colección.

Si bien esto es rápido para una colección pequeña, un escaneo de colección en millones de documentos sería muy lento. En el siguiente paso, solucionará esto agregando un índice.

Crear y Usar un Índice de Campo Único

Ahora que ha visto la ineficiencia de un escaneo de colección, mejoremos el rendimiento creando un índice. Un índice en el campo age permitirá a MongoDB encontrar rápidamente los documentos relevantes sin escanear la colección completa.

Debería seguir en la shell de mongosh del paso anterior.

Cree un índice en el campo age en orden ascendente. El 1 especifica un índice ascendente, mientras que -1 especificaría uno descendente.

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

MongoDB confirmará que el índice se creó correctamente. El nombre predeterminado para este índice será age_1.

Ahora, ejecute exactamente la misma consulta que en el paso anterior y examine su plan de ejecución.

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

Salida de ejemplo (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 los cambios significativos en el plan de ejecución:

  • La stage ahora es IXSCAN, que significa "Index Scan" (Escaneo de Índice). Esto indica que MongoDB utilizó el índice age_1 para encontrar los documentos coincidentes.
  • totalKeysExamined y totalDocsExamined ahora son 3, no 5. MongoDB solo tuvo que examinar los 3 documentos que coincidían con la consulta a través del índice, ignorando los otros 2. Esta es la fuente de la mejora del rendimiento.

Usar un Índice Compuesto para Ordenar

Los índices no solo sirven para acelerar las consultas; también son cruciales para una ordenación eficiente. Cuando ordena por un campo que no está indexado, MongoDB debe realizar la ordenación en memoria, lo que puede ser lento y consumir una cantidad significativa de RAM. Un índice compuesto, que incluye varios campos, puede optimizar las consultas que filtran y ordenan por esos campos.

Creemos un índice compuesto en los campos city (ascendente) y age (descendente). El orden de los campos en el índice es importante para cómo se puede utilizar.

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

Ahora, ejecutemos una consulta que ordene a los usuarios por ciudad y luego por edad. Usaremos .explain() nuevamente para confirmar que el índice se utiliza para la ordenación.

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

Salida de ejemplo (truncada):

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

La etapa IXSCAN muestra que MongoDB utilizó nuestro nuevo índice city_1_age_-1. Dado que los datos ya están ordenados en el índice según nuestros criterios de ordenación, MongoDB no necesita realizar un paso de ordenación separado y costoso en memoria.

Para ver el resultado ordenado real, ejecute la consulta sin .explain().

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

Salida:

[
  { _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' }
]

Los documentos están correctamente ordenados primero por city alfabéticamente y luego por age del más viejo al más joven dentro de cada ciudad, coincidiendo con la definición del índice compuesto.

Gestionar y Eliminar Índices

Si bien los índices mejoran el rendimiento de lectura, no son gratuitos. Consumen espacio de almacenamiento y añaden una pequeña sobrecarga a las operaciones de escritura (inserciones, actualizaciones y eliminaciones). Por lo tanto, es una buena práctica revisar y eliminar periódicamente los índices que ya no se utilizan.

Primero, puede listar todos los índices de una colección utilizando el método getIndexes().

db.users.getIndexes();

Salida:

[
  { "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" }
]

Esto muestra el índice predeterminado en el campo _id, que se crea automáticamente para cada colección, y los dos índices que creamos.

Supongamos que hemos determinado que el índice compuesto city_1_age_-1 ya no es necesario. Puede eliminarlo utilizando el método dropIndex(), pasando el nombre del índice como argumento.

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

MongoDB devolverá un objeto que indica cuántos índices existían antes de la operación de eliminación.

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

Ahora, verifique que el índice se ha eliminado listando los índices nuevamente.

db.users.getIndexes();

Salida:

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

Como puede ver, el índice city_1_age_-1 ha desaparecido. La gestión adecuada de índices es una parte clave para mantener una base de datos saludable y con buen rendimiento.

Para salir de la shell de MongoDB, puede escribir exit o presionar Ctrl+D.

exit;

Resumen

En este laboratorio, ha aprendido las técnicas esenciales para utilizar índices de MongoDB. Comenzó observando un COLLSCAN en una consulta sin índice y comprendió sus limitaciones de rendimiento. Luego creó un índice de un solo campo, lo que cambió el plan de consulta a un IXSCAN mucho más eficiente.

Además, exploró los índices compuestos y vio cómo se pueden utilizar para optimizar las operaciones de ordenación, evitando costosas ordenaciones en memoria. Finalmente, aprendió a gestionar sus índices listándolos con getIndexes() y eliminando los que no se utilizan con dropIndex(). Estas habilidades son fundamentales para crear aplicaciones rápidas y escalables con MongoDB.