La optimización de queries mejora la eficiencia de las operaciones de lectura al reducir la cantidad de datos que las operaciones de queries deben procesar. Se pueden utilizar índices, proyecciones y límites de queries para mejorar el rendimiento de los queries y reducir el consumo de recursos.
Crear índices para brindar soporte a queries
Crear Índices para las consultas que se plantean con frecuencia. Si una consulta busca en varios campos, crea un índice compuesto. El uso de un índice mejora el rendimiento porque, sin un índice, la query debe escanear todos los documentos de la colección.
Por ejemplo, considera la siguiente query sobre el rated campo en la colección movies:
let ratingValue = <someUserInput>; db.movies.find( { rated: ratingValue } );
Para mejorar el rendimiento de esta query, añade un índice a la colección movies en el campo rated. [1] en
mongosh, utiliza el método db.collection.createIndex() para crear índices:
db.movies.createIndex( { rated: 1 } )
Para analizar el rendimiento del query, consultar Interpretar los resultados del plan de ejecución.
| [1] | Para los índices de un solo campo, el orden del índice no importa. Para los índices compuestos, el orden de los campos influye en los tipos de consultas que soporta el índice. Para obtener más detalles, consulta Orden de clasificación del índice compuesto. |
Crear queries selectivas
La selectividad del query se refiere a qué tan bien el predicado del query filtra los documentos de una colección. La selectividad del query determina si las queries pueden usar los índices de manera efectiva.
Los queries más selectivos coinciden con un porcentaje menor de documentos. Por ejemplo, una coincidencia exacta en el campo único _id es altamente selectiva, ya que puede coincidir como máximo con un documento.
Las consultas menos selectivas coinciden con un mayor porcentaje de documentos y no pueden utilizar los índices de manera eficaz.
Por ejemplo, los operadores de desigualdad $nin y $ne no son muy selectivos, ya que a menudo coinciden con una gran parte del índice. Como resultado, en muchos casos, un query $nin o $ne con un índice puede no tener un mejor desempeño que un query $nin o $ne que debe escanear todos los documentos de una colección.
La selectividad de regular expressions depende de las expresiones en sí mismas. Para aprender más, se puede consultar uso de expresiones regulares e índice.
Solo datos necesarios del proyecto
Cuando se necesita un subconjunto de campos de documentos, se puede mejorar el rendimiento devolviendo solo los campos que son necesarios. Las proyecciones disminuyen el tráfico de red y el tiempo de procesamiento.
Por ejemplo, si el query para la colección movies solo necesita los campos year, title, directors y plot, se deben especificar esos campos en la proyección:
db.movies.find( {}, { year: 1, title: 1, directors: 1 } ).sort( { year: -1 } ).limit(3)
Cuando se usa una etapa de agregación $project, normalmente debería ser la última etapa del pipeline y se utiliza para especificar qué campos devolver al cliente.
Usar una etapa $project al principio o en medio de un pipeline para reducir el número de campos que se pasan a las etapas posteriores del pipeline es poco probable que mejore el rendimiento, ya que la base de datos realiza esta optimización automáticamente.
Para aprender más sobre el uso de las proyecciones, consultar Campos de proyecto a devolver del query.
Ejemplo
Para lograr un query cubierto, se deben indexar los campos proyectados. La regla ESR (Equality, Sort, Range) [Igualdad, Orden, Rango] se aplica al orden de los campos en el índice.
Por ejemplo, considera el siguiente índice en una colección movies:
db.movies.createIndex( { rated: 1, _id: 1, "imdb.rating": 1, title: 1, released: 1 } )
El índice anterior, si bien es técnicamente correcto, no está estructurado para optimizar el rendimiento de las query.
La siguiente query utiliza la regla ESR (Igualdad, Orden, Rango) para estructurar una pipeline de agregación más eficiente y mejorar los tiempos de respuesta de la query.
db.movies.aggregate( [ { $match: { rated: "PG", released: { $gt: ISODate("2000-01-01T00:00:00Z") } } }, { $sort: { title: 1 } }, { $limit: 5 }, { $project: { _id: 1, "imdb.rating": 1 } } ] )
El índice y el query siguen la regla ESR:
ratedse utiliza para una coincidencia exacta (E), por lo que es el primer campo en el índice.titlese utiliza para ordenar (S), por lo que está después derateden el índice.releasedse utiliza para una query de rango (R), por lo que es el último campo del índice.
Limitar los resultados del query
Los cursores de MongoDB devuelven resultados en agrupación. Si sabe cuántos resultados se desean, se puede especificar ese valor en el método limit(). Limitar los resultados reduce la demanda sobre los recursos de la red.
Ordene los resultados antes de aplicar un límite para garantizar que la query devuelva los documentos esperados. Por ejemplo, si solo necesitas 10 resultados de tu query en la colección movies, ejecuta la siguiente query:
db.movies.find( {}, { title: 1, year: 1 } ).sort( { year: -1 } ).limit(10)
Para aprender más sobre cómo limitar los resultados, se puede consultar limit().
Utilice sugerencias de índices
El optimizador del query normalmente selecciona el índice óptimo para una operación específica. Sin embargo, se puede forzar a MongoDB a usar un índice específico utilizando el método hint(). Utiliza hint() para respaldar las pruebas de rendimiento o cuando consultes un campo que aparece en varios índices para garantizar que MongoDB utilice el índice correcto.
Utilizar operaciones del servidor.
Utiliza el operador $inc para incrementar o decrementar valores en los documentos. El operador incrementa el valor del campo en el lado del servidor, como una alternativa a la selección de un documento, la realización de cambios en el código del cliente y luego la escritura de todo el documento en el servidor. El operador $inc también puede ayudar a evitar condiciones de carrera que se producen cuando dos instancias de la aplicación query un documento, incrementan manualmente un campo y guardan todo el documento a la vez.
Ejecutar queries cubiertos
Una query cubierta es aquella que puede satisfacerse completamente utilizando un índice y no necesita examinar ningún documento. Un índice cubre una query cuando se cumplen todas las siguientes condiciones:
Todos los campos del query (tanto los especificados por la aplicación como los necesarios internamente, como para fines de partición) son parte de un índice.
Todos los campos devueltos en los resultados están en el mismo índice.
Ningún campo en la query es igual a
null. Por ejemplo, los siguientes predicados de query no pueden resultar en queries cubiertas:{ "field": null }{ "field": { $eq: null } }
Ejemplo
Una colección de movies tiene el siguiente índice en los campos rated y title:
db.movies.createIndex( { rated: 1, title: 1 } )
El índice cubre la siguiente operación que hace queries en los campos rated y title y devuelve solo el campo title:
db.movies.find( { rated: "PG", title: /^T/ }, { title: 1, _id: 0 } ).limit(3)
Para que el índice especificado cubra la query, el documento de proyección debe especificar explícitamente _id: 0 para excluir el campo _id del resultado, ya que el índice no incluye el campo _id.
Documentos incrustados
Un índice puede cubrir un query sobre campos dentro de documentos incrustados.
Por ejemplo, considere la colección theaters del sample_mflix conjunto de datos, que tiene documentos con la siguiente estructura:
{ theaterId: <num>, location: { address: { street1: "<address>", city: "<city>", state: "<state>", zipcode: "<zip>" }, geo: { ... } } }
La colección tiene el siguiente índice:
db.theaters.createIndex( { "location.address.city": 1 } )
El índice { "location.address.city": 1 } cubre el siguiente query:
db.theaters.find( { "location.address.city": "Portland" }, { "location.address.city": 1, _id: 0 } ).limit(1)
Nota
Para indexar campos en documentos incrustados, se debe utilizar notación de puntos. Consultar Crear un Índice en un campo incrustado.
Cubrir multiclaves
Los índices multiclave pueden cubrir queries sobre campos que no son arreglos si el índice rastrea qué campo o campos hacen que el índice sea multiclave.
Los índices de múltiples claves no pueden cubrir queries sobre campos de arreglos.
Para ver un ejemplo de un query cubierto con un índice multiclave, se puede consultar Queries cubiertos en la página de índices multiclave.
Rendimiento
Debido a que el índice contiene todos los campos requeridos por el query, MongoDB puede hacer coincidir las condiciones del query y devolver los resultados utilizando solo el índice.
Consultar únicamente el índice puede ser mucho más rápido que consultar documentos fuera del índice. Las claves de índice suelen ser más pequeñas que los documentos que catalogan, y los índices suelen estar disponibles en la RAM o ubicados secuencialmente en el disco.
Limitaciones
Tipos de índice
No todos los tipos de índice pueden cubrir queries. Para obtener detalles sobre el soporte de índices cubiertos, se puede consultar la página de documentación del tipo de índice correspondiente.
Colecciones fragmentadas
Cuando se ejecuta en mongos, los índices solo pueden cubrir queries en las colecciones particionadas si el índice contiene la clave de partición.
Explique los resultados
Para determinar si un query es un query cubierto, se debe utilizar el método db.collection.explain() o el método explain(). Ver Queries cubiertos.