Curso
En cualquier base de datos, incluida MongoDB, los índices son clave en el proceso de desarrollo. Sin ellos, a medida que crece tu base de datos, el rendimiento se resiente. Con índices bien definidos, el acceso a los datos se mantiene óptimo aunque el volumen aumente.
En este artículo vamos a revisar los distintos tipos de índices disponibles en MongoDB, algunos consejos para sacarles partido y cómo asegurarte de que los estás aprovechando de verdad. Si estás empezando con MongoDB, te recomendamos el curso Introduction to MongoDB.
¿Dónde importan los índices en MongoDB y por qué?
Cuando hablamos de índices, hablamos de rendimiento. La diferencia está entre revisar cada documento de una colección para ver si cumple la consulta, o acotar la búsqueda a un subconjunto más específico que sí la cumple. Si la colección es pequeña, el rendimiento será bueno en ambos casos. Pero si tienes 100.000 documentos y solo 100 coinciden con tu consulta, sin un índice adecuado tardarás más en encontrar esos 100 entre los 100.000.
En MongoDB hay varios tipos de índices:
- De un solo campo: un índice sobre un único campo del documento. El índice por defecto del campo
_ides un índice de un solo campo. - Compuesto: un índice basado en varios campos del documento, donde el orden de los campos en la consulta y en el índice importa.
- Multiclave: índice para campos de tipo array.
- Comodín: índice que admite campos cuyos nombres pueden variar ligeramente entre documentos. No rinde tan bien como otros tipos de índices.
- Geoespacial: índice para acelerar consultas geoespaciales que usan datos de coordenadas de latitud y longitud.
- Hasheado: índice especialmente útil con claves de particionado (shard keys).
Para más información sobre los tipos de índice, consulta la documentación de MongoDB.
Lo más habitual es que utilices índices de un solo campo, compuestos o multiclave, pero al final depende del tipo de aplicación, de las consultas y de los datos.
Los índices importan en cualquier operación que recupera documentos. No se limita a find o findOne. Al actualizar o eliminar documentos, también usas criterios de coincidencia que la base de datos aplica para localizar los documentos antes de actualizar o borrar.
Los operadores find y findOne no son los únicos que se benefician de un índice. Si usas $match como primera etapa en un pipeline de agregación, un índice acelera el proceso de forma notable.
Consejos y trucos de indexación en MongoDB para tener éxito
Crear índices al azar puede ser tan ineficiente como no usar ninguno. Ten en cuenta estos puntos para construir una aplicación mucho más eficiente.
Igualdad, ordenación, rango (ESR) para índices compuestos
La regla general al crear un índice compuesto es definir cómo usarás cada campo en la consulta y asegurarte de seguir la buena práctica ESR.
¿Qué significa ESR? Desgranémoslo:
- Igualdad: campos que se comparan por coincidencia exacta, no aproximada. Por ejemplo, buscar por el nombre exacto de una persona sería una etapa de igualdad.
- Ordenación: campos que usarás para ordenar ascendente o descendentemente.
- Rango: campos que vas a filtrar sin coincidencia exacta. Por ejemplo, con
$lto$gtcoincidirás con documentos dentro de un rango, no con un valor concreto.
Para verlo más claro, fíjate en esta consulta:
db.employee.find(
{
"department": "Developer Relations",
"salary": {
"$lt": 80000
}
}
).sort(
{
"name": 1
}
);
Esta consulta hace una coincidencia por igualdad en el departamento, una por rango en el salario y ordena los resultados por el nombre.
Siguiendo ESR, el índice sería algo así:
db.employee.createIndex({
"department": 1,
"name": 1,
"salary": 1
});
Queremos seguir ESR para evitar ordenaciones en memoria y acotar el espacio de búsqueda para ejecutar la consulta con buen rendimiento.
Consultas cubiertas
En general, indexar reduce el conjunto de documentos a escanear al ejecutar una consulta y, por tanto, mejora el rendimiento. Si quieres llevarlo un paso más allá, apuesta por las consultas cubiertas. En una consulta cubierta, todos los resultados se obtienen directamente del índice, sin tener que leer documentos de la colección.
Hay algunas condiciones que pueden hacer que una consulta cubierta sea o no posible en tu aplicación:
- El criterio de coincidencia y la ordenación deben ajustarse exactamente a cómo se creó el índice.
- Los campos que solicitas en el conjunto de resultados también deben estar en el índice.
- Si hay particionado (sharding), la shard key debe formar parte del índice.
Entonces, ¿qué implica todo esto?
Por ejemplo, crea este índice:
db.department.createIndex({
"country": 1,
"salary": 1
});
En este ejemplo, indexamos por los campos country y salary. Luego podemos ejecutar esta consulta:
db.department.find(
{
"country": "US"
},
{
"country": 1,
"salary": 1
}
);
Esta consulta, que incluye una proyección, usará el índice, pero no será una consulta cubierta. MongoDB devuelve el campo _id por defecto y en este ejemplo no lo hemos incluido en el índice. Podemos cambiar el índice o la consulta. Si cambiamos la consulta, haríamos esto:
db.department.find(
{
"country": "US"
},
{
"country": 1,
"salary": 1,
"_id": 0
}
);
Recuerda: para usar una consulta cubierta, el índice debe ser válido y los campos solicitados deben estar en el índice. Si no, solo localizarás documentos a través del índice, pero no desde el propio índice.
Puedes comprobar si estás usando una consulta cubierta con el argumento executionStats del operador explain que vemos más abajo. El valor totalDocsExamined te lo dirá: si es cero, estás usando una consulta cubierta.
Reducir o eliminar índices innecesarios
Los índices no son gratis en términos de recursos del servidor. El servidor debe mantener cada índice y ese mantenimiento usa CPU, RAM y disco. Cuantos más índices crees, más sobrecarga generarás, lo que puede traducirse en más costes o menos rendimiento si el servidor está justo de recursos. Este impacto no solo afecta a lecturas: también puede ralentizar escrituras y borrados porque MongoDB consume más recursos.
Lo ideal es crear índices solo para consultas de alta frecuencia. ¿De verdad necesita índice esa consulta que se ejecuta una vez al mes? Probablemente no. ¿Y la que corre cada 10 segundos? ¡Sin duda!
Descubre más buenas prácticas de indexación en el blog de MongoDB.
¿Funciona tu índice de MongoDB?
Que hayas creado un índice no significa que tu consulta lo esté usando. En MongoDB puedes diagnosticar qué índice se utiliza en cada consulta.
Por ejemplo, crea este índice:
db.people.createIndex({
"name": 1,
"is_active": 1
});
En este ejemplo indexamos los campos name e is_active. Ahora ejecutamos esta consulta:
db.people.find({
"is_active": true
});
La consulta anterior se ejecutará con o sin índice, y si el conjunto de datos es pequeño, será rápida igualmente. Pero si queremos saber si usa nuestro índice, podemos ajustar la consulta:
db.people.find({
"is_active": true
}).explain();
Ahora usamos explain para obtener el plan de ejecución. En él podemos ver algo como lo siguiente:
winningPlan: {
isCached: false,
stage: 'COLLSCAN',
filter: { is_active: { '$eq': true } },
direction: 'forward'
}
Para esta consulta, MongoDB hizo un "COLLSCAN", es decir, no usó nuestro índice aunque el campo coincidiera. Si cambiamos la consulta por algo así:
db.people.find({
"name": "Nic Raboy",
"is_active": true
}).explain();
El resultado de explain cambia:
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]' ]
}
}
},
Ahora el plan indica un "IXSCAN" y especifica el nombre del índice usado, en este caso name_1_is_active_1. El nombre se definió automáticamente al crear el índice.
Para una operación de update, el proceso es similar salvo por dónde colocamos explain.
Una operación update podría ser así:
db.people.explain().update(
{
“name”: “Nic Raboy”,
“Is_active”: true
},
{
“$set”: { “is_active”: false }
}
);
Fíjate que aquí explain va antes de la función update.
Si tu operación de actualización usa el índice esperado, verás "IXSCAN" junto con el nombre del índice. Encontrarás más información sobre operaciones de actualización y su uso de índices.
Otra forma de ver los detalles del plan de ejecución es usar runCommand en la base de datos. Aunque es menos legible, es muy potente y sería algo así:
db.runCommand({
"explain": {
"update": "people",
"updates": [
{
"q": {
"name": "Nic Raboy",
"is_active": true
},
"u": {
"$set": { "is_active": false }
}
}
]
},
"verbosity": "allPlansExecution"
});
Esto funciona igual que la operación de actualización con explain.
Si no tienes claro cómo crear índices, o notas que tus consultas van más lentas de lo esperado, revisa los detalles de ejecución.
Conclusión
Acabas de ver algunas buenas prácticas de indexación en MongoDB. Aunque no hemos cubierto todos los escenarios posibles, esto te servirá como punto de partida para que tus usuarios disfruten del mejor rendimiento al trabajar con tus datos.
Recapitulando:
- Sigue la regla de igualdad, ordenación y rango (ESR) siempre que puedas.
- Si es posible, utiliza consultas cubiertas.
- No te pases creando índices: identifica qué consultas se ejecutan más y cuántos datos recuperan.
- Apóyate en el operador
explainpara probar tus índices y su rendimiento.
Estos puntos no son una lista exhaustiva de buenas prácticas de indexación. Hay otros factores, como las cantidades de cardinalidad.
Para más ayuda con índices, consulta la documentación de MongoDB. Si usas un plan de pago en MongoDB Atlas, valora las herramientas del performance adviser para recomendaciones de índices y consultas.
Preguntas frecuentes sobre indexación en MongoDB
¿Cuántos índices debo crear en una colección de MongoDB?
Depende. Debes encontrar el equilibrio que te funcione, pero valora indexar solo las consultas que se ejecutan con frecuencia frente a las ocasionales.
He creado varios índices, pero mis consultas siguen siendo lentas. ¿Por qué?
¿Seguro que tus consultas están usando los índices que creaste? Utiliza el operador explain o las herramientas de asesoría de MongoDB Atlas para diagnosticar.
Para la regla ESR, ¿el orden de los campos en el índice debe ser exactamente igualdad, luego ordenación y luego rango?
Aunque MongoDB a veces puede usar un índice fuera de orden, es más eficaz cuando el orden de las claves del índice sigue la secuencia de la consulta: primero filtros de igualdad, luego ordenaciones y por último rangos.
¿Cuáles son los diferentes modos de explain?
Por defecto, explain() se ejecuta en modo "queryPlanner", pero también tienes los modos "executionStats" y "allPlansExecution".

Nic Raboy es Jefe de Relaciones con Desarrolladores en MongoDB, donde dirige un equipo de programadores de Python, Java, C# y PHP que crean contenido impresionante para ayudar a los programadores a incluir con éxito MongoDB en sus proyectos. Tiene experiencia con Golang y JavaScript y escribe a menudo sobre muchas de sus aventuras de desarrollo.
