Conozca cómo funcionan los planes de consulta de MongoDB, por qué ocurre la replanificación y cómo resolver problemas relacionados con este proceso.
Planificador de consultas
El planificador de consultas de MongoDB selecciona y almacena en caché el plan de consulta más eficiente para cada consulta, según los índices disponibles.
El planificador de consultas evalúa los planes candidatos según la cantidad de unidades de trabajo (works) que requiere cada uno y luego almacena en caché el plan ganador. Las entradas almacenadas en caché se reutilizan para consultas con la misma forma de consulta.
Las entradas de la caché de planes tienen tres estados:
Missing: No existe ningún plan en la caché.
Inactive: El plan existe en la caché con un valor
worksevaluado y puede promoverse a activo.Active: Es el plan ganador en la caché. Puede degradarse a inactivo.
La caché de planes se almacena en memoria y no se conserva en disco. Se borra cuando se reinicia la instancia de MongoDB o cuando se elimina una colección o un índice. La caché tiene un límite de tamaño y utiliza un mecanismo de desalojo LRU.
Utilice los siguientes comandos para gestionar los planes de consulta:
-
Borre la caché de planes de una colección.
db.<collection>.getPlanCache().clear() -
Liste todas las formas de consulta de una colección.
db.<collection>.getPlanCache().list()NotaPlanCache.listQueryShapes()está obsoleto desde MongoDB 4.2. UtilicePlanCache.list()en su lugar. -
Consulte los planes almacenados en caché para una forma de consulta específica.
db.<collection>.getPlanCache().list([{ $match: { "createdFromQuery.query": { "name": "testname" }, "createdFromQuery.sort": { "name": 1 } } }])NotaPlanCache.getPlansByQuery()está obsoleto desde MongoDB 4.2. UtilicePlanCache.list()con una etapa$matchpara filtrar por forma de consulta.
QueryHash y planCacheKey
A partir de MongoDB 4.2, cada forma de consulta obtiene un queryHash único. La clave planCacheKey depende tanto de la forma de consulta como de los índices disponibles. Si agrega o elimina un índice compatible con la forma de consulta, cambia el valor de planCacheKey, pero no el de queryHash.
Por ejemplo, considere una colección con los siguientes índices y formas de consulta:
-
Índices
db.foo.createIndex( { x: 1 } ) db.foo.createIndex( { x: 1, y: 1 } ) db.foo.createIndex( { x: 1, z: 1 }, { partialFilterExpression: { x: { $gt: 10 } } } ) -
Formas de consulta
db.foo.explain().find( { x: { $gt: 5 } } ) // Query operation 1 db.foo.explain().find( { x: { $gt: 20 } } ) // Query operation 2
El tercer índice es compatible con la consulta 2, pero no con la consulta 1; por lo tanto, ambas consultas tienen claves de caché de planes diferentes. Si agrega el índice {x:1, a:1}, se actualizan ambas claves de caché.
Replanificación de consultas
A medida que cambian los datos de una colección, un plan de consulta almacenado en caché puede volverse subóptimo y requerir adaptación.
Cuando una consulta coincide con un plan almacenado en caché, el planificador lo reutiliza directamente mientras monitorea el rendimiento. Si el plan en caché resulta significativamente menos eficiente que una alternativa (por ejemplo, más de 10 veces más lento), el planificador aborta la ejecución, desaloja el plan y vuelve a evaluar todos los candidatos. Este proceso se denomina replanificación de consultas.
Impacto y soluciones
La palabra clave "replanned":true en el registro de consultas lentas indica que el planificador no logra encontrar un plan consistentemente eficaz para una forma de consulta específica.
Ejemplo de entrada en el registro de consultas lentas:
"replanned":true,"replanReason":"cached plan was less efficient than expected: expected trial execution to take X works but it took at least 10X works"
Impactos
Degrada el rendimiento de las consultas.
Provoca contención de bloqueos mutex y un alto uso de CPU.
Soluciones
upgrade the instance specification temporalmente para aliviar la carga de la base de datos.
Borre la caché de planes y compruebe si el planificador selecciona un plan mejor.
-
Utilice
hint()en su aplicación para especificar un índice en las condiciones de consulta que causan la replanificación:db.<collection>.find({a:"ABC"},{b:1,_id:0}).sort({c:1}).hint({ a: 1, c: 1, b: 1} )NotaUn hint anula el comportamiento predeterminado del planificador de consultas.
-
Utilice un filtro de índice para restringir los índices en las consultas que desencadenan la replanificación:
// Check if the index exists db.<collection>.getIndexes() // Set the index filter db.runCommand( { planCacheSetFilter: "<collection>", query: { a: "ABC" }, projection: { b: 1, _id: 0 }, sort: { c: 1 }, indexes: [ { a: 1, c: 1 , b: 1 } ] } ) // Remove the previous filter db.runCommand( { planCacheClearFilters: "<collection>" } )NotaLos filtros de índice anulan el comportamiento predeterminado del planificador de consultas.
Si una consulta tiene tanto un hint como un filtro de índice, el filtro de índice tiene prioridad. Utilice los filtros de índice con precaución. Index Filters.
El optimizador de consultas utiliza un escaneo de colección (COLLSCAN) o el índice especificado en
planCacheSetFilter. Si el índice especificado no existe o está oculto, el optimizador recurre a un escaneo de colección, lo cual puede provocar un alto uso de CPU y picos de E/S. Antes de ejecutarplanCacheSetFilter, utilicedb.<collection>.getIndexes()para comprobar que el índice existe. planCacheSetFilter.
-
(Recomendado) Optimice sus consultas e índices para evitar la replanificación.
NotaLos hints y los filtros de índice son soluciones temporales. Revisar sus consultas, índices y esquema de documentos generalmente produce mejores resultados.
(Recomendado) Para instancias que ejecutan la versión principal 4.2 o 4.4, actualice a la última versión menor del kernel para reducir la contención de bloqueos mutex (SERVER-40805), o bien actualice a la versión principal 5.0 o 6.0. Upgrade the minor version of a database. Upgrade the major version of a database.
Si ninguna de estas soluciones resuelve el problema, puede enviar un ticket para obtener soporte técnico.