Entenda o funcionamento dos planos de consulta do MongoDB, os motivos do replanejamento e como resolver problemas relacionados.
Planejador de consultas
O planejador de consultas do MongoDB seleciona e armazena em cache o plano mais eficiente para cada consulta com base nos índices disponíveis.
O planejador avalia os planos candidatos pela quantidade de unidades de trabalho (works) necessárias e armazena o plano vencedor em cache. As entradas em cache são reutilizadas para consultas com o mesmo formato.
As entradas do cache de plano possuem três estados:
Ausente (Missing): Não existe nenhum plano no cache.
Inativo (Inactive): O plano existe no cache com um valor
worksavaliado e pode ser promovido a ativo.Ativo (Active): Representa o plano vencedor no cache. Pode ser rebaixado para inativo.
O cache de plano reside na memória e não persiste em disco. Ele é limpo quando a instância do MongoDB reinicia ou quando uma coleção ou índice é excluído. O cache possui limite de tamanho e utiliza evicção LRU.
Use os comandos abaixo para gerencie planos de consulta:
-
Limpe o cache de plano de uma coleção.
db.<collection>.getPlanCache().clear() -
Liste todos os formatos de consulta de uma coleção.
db.<collection>.getPlanCache().list()NotaO método
PlanCache.listQueryShapes()está obsoleto desde o MongoDB 4.2. UsePlanCache.list()em seu lugar. -
Visualize os planos em cache para um formato de consulta específico.
db.<collection>.getPlanCache().list([{ $match: { "createdFromQuery.query": { "name": "testname" }, "createdFromQuery.sort": { "name": 1 } } }])NotaO método
PlanCache.getPlansByQuery()está obsoleto desde o MongoDB 4.2. UsePlanCache.list()com um estágio$matchpara filtrar por formato de consulta.
QueryHash e planCacheKey
A partir do MongoDB 4.2, cada formato de consulta recebe um queryHash exclusivo. A chave planCacheKey depende tanto do formato da consulta quanto dos índices disponíveis. Adicionar ou remover um índice compatível com o formato de consulta altera a planCacheKey, mas não modifica o queryHash.
Considere uma coleção de exemplo com os seguintes índices e formatos 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 } } } ) -
Formatos de consulta
db.foo.explain().find( { x: { $gt: 5 } } ) // Query operation 1 db.foo.explain().find( { x: { $gt: 20 } } ) // Query operation 2
O terceiro índice atende à consulta 2, mas não à consulta 1; portanto, as duas consultas possuem chaves de cache de plano diferentes. Adicionar o índice {x:1, a:1} atualiza ambas as chaves de cache.
Replanejamento de consultas
À medida que os dados da coleção mudam, um plano de consulta em cache pode se tornar subótimo e exigir adaptação.
Quando uma consulta corresponde a um plano em cache, o planejador o reutiliza diretamente enquanto monitora o desempenho. Caso o plano em cache se torne significativamente menos eficiente (por exemplo, mais de 10 vezes mais lento) que uma alternativa, o planejador interrompe a execução, remove o plano e reavalia todos os candidatos. Esse processo chama-se replanejamento de consulta.
Impacto e soluções
A presença da palavra-chave "replanned":true no log de consultas lentas indica que o planejador não consegue encontrar um plano consistentemente eficaz para um formato de consulta específico.
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.
Provoca contenção de locks de mutex e alta utilização da CPU.
Soluções
Faça upgrade temporário da especificação da instância para aliviar a carga do banco de dados.
Limpe o cache de plano e verifique se o planejador seleciona um plano melhor.
-
Use
hint()na aplicação para especifique um índice nas 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 planejador de consultas.
-
Configure um filtro de índice para restringir os índices usados 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>" } )NotaFiltros de índice substituem o comportamento padrão do planejador de consultas.
Se uma consulta possuir simultaneamente um hint e um filtro de índice, o filtro de índice terá precedência. Use filtros de índice com cautela. Index Filters.
O otimizador de consultas utiliza uma varredura de coleção (COLLSCAN) ou o índice especificado em
planCacheSetFilter. Se o índice especificado não existir ou estiver oculto, o otimizador recorrerá a uma varredura de coleção, o que pode causar alto uso de CPU e picos de I/O. Antes de executarplanCacheSetFilter, usedb.<collection>.getIndexes()para verifique se o índice existe. planCacheSetFilter.
-
(Recomendado) Otimize suas consultas e índices para evitar replanejamento.
NotaHints e filtros de índice são soluções paliativas. Revisar suas consultas, índices e esquema de documentos geralmente produz resultados melhores.
(Recomendado) Para instâncias executando a versão principal 4.2 ou 4.4, atualize para a versão secundária mais recente do kernel a fim de reduzir a contenção de locks de mutex (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.
Caso nenhuma dessas soluções resolva o problema, envie um ticket para obter suporte técnico.