Entenda como funcionam os planos de consulta do MongoDB, por que o replanejamento ocorre e como resolver problemas relacionados.
Query planner
O query planner do MongoDB seleciona e armazena em cache o plano de consulta mais eficiente para cada consulta com base nos índices disponíveis.
O query planner avalia os planos candidatos pelo número de unidades de trabalho (works) que cada um exige e, em seguida, armazena o plano vencedor em cache. As entradas em cache são reutilizadas para consultas com o mesmo query shape.
As entradas do plan cache têm três estados:
Missing: Nenhum plano existe no cache.
Inactive: O plano existe no cache com um valor
worksavaliado e pode ser promovido para ativo.Active: O plano vencedor no cache. Pode ser rebaixado para inativo.
O plan cache é armazenado em memória e não persiste em disco. O cache é apagado quando a instância do MongoDB é reiniciada ou quando uma coleção ou índice é removido. O cache tem um limite de tamanho e usa evicção LRU.
Use os seguintes comandos para gerenciar planos de consulta:
-
Limpe o plan cache de uma coleção.
db.<collection>.getPlanCache().clear() -
Liste todos os query shapes de uma coleção.
db.<collection>.getPlanCache().listQueryShapes() -
Visualize os planos em cache para um query shape.
db.<collection>.getPlanCache().getPlansByQuery({"query": {"name": "testname"}, "sort": { "name": 1 })
QueryHash e planCacheKey
A partir do MongoDB 4,2, cada query shape recebe um queryHash exclusivo. O planCacheKey depende tanto do query shape quanto dos índices disponíveis. Adicionar ou remover um índice compatível com o query shape altera o planCacheKey, mas não o queryHash.
Exemplo de coleção com os seguintes índices e query shapes:
-
Índices
db.foo.createIndex( { x: 1 } ) db.foo.createIndex( { x: 1, y: 1 } ) db.foo.createIndex( { x: 1, z: 1 }, { partialFilterExpression: { x: { $gt: 10 } } } ) -
Query shapes
db.foo.explain().find( { x: { $gt: 5 } } ) // Query operation 1 db.foo.explain().find( { x: { $gt: 20 } } ) // Query operation 2
O terceiro índice é compatível com a consulta 2, mas não com a consulta 1; portanto, as duas consultas têm plan cache keys diferentes. Adicionar o índice {x:1, a:1} atualiza ambas as cache keys.
Replanejamento de consultas
À medida que os dados da coleção mudam, um plano de consulta em cache pode se tornar subótimo e precisar ser adaptado.
Quando uma consulta corresponde a um plano em cache, o planner o reutiliza diretamente enquanto monitora o desempenho. Se o plano em cache se tornar significativamente menos eficiente — por exemplo, mais de 10 vezes mais lento — do que uma alternativa, o planner interrompe a execução, remove o plano do cache e reavalia todos os candidatos. Esse processo é chamado de replanejamento de consultas.
Impactos e soluções
A palavra-chave "replanned":true no log de consultas lentas indica que o planner não consegue encontrar um plano consistentemente eficaz para um determinado query shape.
Exemplo de entrada no log 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 o desempenho das consultas.
Causa contenção de mutex lock e alta utilização de CPU.
Soluções
Temporariamente, atualize a especificação da instância para reduzir a carga no banco de dados.
Limpe o plan cache e verifique se o planner seleciona um plano melhor.
-
Use
hint()na sua aplicação para especificar um índice para condições de consulta que causam replanejamento:db.<collection>.find({a:"ABC"},{b:1,_id:0}).sort({c:1}).hint({ a: 1, c: 1, b: 1} )NotaUm hint substitui o comportamento padrão do query planner.
-
Use um index filter para restringir os índices em consultas que disparam replanejamento:
// 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>" } )NotaIndex filters substituem o comportamento padrão do query planner.
Se uma consulta tiver tanto um hint quanto um index filter, o index filter tem precedência. Use index filters com cautela. Index Filters.
O otimizador de consultas usa um collection scan (COLLSCAN) ou o índice especificado em
planCacheSetFilter. Se o índice especificado não existir ou estiver oculto, o otimizador recorre a um collection scan, o que pode causar alto uso de CPU e picos de I/O. Antes de executarplanCacheSetFilter, usedb.<collection>.getIndexes()para verificar se o índice existe. planCacheSetFilter.
-
(Recomendado) Otimize suas consultas e índices para evitar o replanejamento.
NotaHints e index filters são recursos paliativos. Revisar suas consultas, índices e o schema dos documentos geralmente produz resultados mais eficazes.
(Recomendado) Para instâncias com versão principal 4,2 ou 4,4, atualize para a versão secundária de kernel mais recente a fim de reduzir a contenção de mutex lock (SERVER-40805), ou faça upgrade para a versão principal 5,0 ou 6,0. Atualize a versão secundária do banco de dados. Atualize a versão principal do banco de dados.
Se nenhuma dessas soluções resolver o problema, envie um ticket para obter suporte técnico.