Tecnologia
O que é e como funciona o otimizador de consultas do MongoDB
5 minutos de leitura
Neste artigo, quero mostrar como funciona o otimizador de consultas do banco de dados MongoDB, o que são os planos de execução e como você pode visualizar o que Mongo está fazendo por debaixo do capô.
Para isso vamos realizar um tutorial com algumas consultas simples e usar o comando explain() para entender como tudo isso funciona.
Objetivos
- Entender como o banco MongoDB chega nos dados a partir de uma consulta;
- Conhecer o componente otimizador de consultas do MongoDB;
- Conhecer os planos de execução;
- Aprender a usar o comando .explain();
Pré-requisitos
- Ter um banco MongoDB versão >5.0 instalado;
- Ter o aplicativo Nosqlbooster instalado para realizar as consultas;
- Conhecimentos básicos de mongo (comandos find, count e sort já são suficientes;
Bora começar?
O que é o otimizador de consultas no MongoDB
Quero começar explicando que ao realizar uma consulta em um banco MongoDB, o banco não vai simplesmente nos arquivos pegar os dados que você solicitou através de uma determinada query.
Existe esse componente chamado otimizador de consultas que na primeira vez que é realizada uma certa pesquisa já monta vários planos de execução, que são maneiras de chegar aos dados. E a partir de um cálculo de custo determina qual é o melhor plano.
O que são os planos de execução
Uma vez o banco tendo escolhido o “melhor plano de execução”, na segunda vez que a query é executada, o banco simplesmente usará esse plano para chegar rapidamente aos dados e retorná-los ao usuário. Em outras palavras, a cada consulta o banco separa o “O QUE” o usuário está pedindo. No caso, os DADOS, do COMO o banco vai conseguir esses dados (plano de execução) e o responsável por fazer isso (QUEM) é o otimizador de Consultas.
Resumindo:
O QUE ==> Dados
COMO ==> Plano de Execução
QUEM ==> Otimizador de Consultas
Como usar o Explain em uma query simples
Agora que já entendemos o que é o otimizador de consultas e os planos de execução, vamos fazer um exemplo simples para ver na prática como isso funciona.
Passo 1: Criar uma massa de dados simples
Para conseguirmos ver como funciona o otimizador de queries do mongo, é preciso ter uma base de dados simples. Por isso os comandos abaixo vão importar o arquivo pessoas.json criando um banco chamado tutorial e uma collection chamado pessoas.
mongoimport --drop -c pessoas --uri mongodb://localhost:27020/tutorial pessoas.json
E aqui um exemplo de um registro dessa base de dados:
{
"_id" : ObjectId("6318cc7c0b4ddd7bbd7fa627"),
"usuario" : "leandro_santos",
"nome_completo" : "Leandro Santos",
"endereco" : {
"rua" : "Av. Ipiranga",
"numero" : "",
"cidade" : "Porto Alegre",
"estado" : "RS",
"complemento" : ""
},
"email" : "[email protected]",
"nascimento" : "2013-08-12T18:54:21.000Z"
}
Passo 2: Verificando importação + tamanho da base
Agora que já temos nossa base de dados pronta, vamos ver que tamanho nossa collection tem. Para isso vamos usar o comando .count() + find()
db.pessoas.find({}).count()
db.pessoas.count()
Resultado: 20868 documentos nessa collection.
Explicando: o comando .count() sendo chamado tanto após uma consulta (exemplo 1) ou sendo executado diretamente sobre uma collection (exemplo 2), mostra o número de documentos retornados.
Passo 3: Executar uma query simples
Agora vamos supor que queremos saber todas as pessoas que moram em Porto Alegre. Para isso fazemos o seguinte:
db.pessoas.find({"endereco.cidade": "Porto Alegre"})
ou então usando o count:
db.pessoas.find({"endereco.cidade": "Porto Alegre"}).count()
É possível ver que existem 7.749 pessoas morando em Porto Alegre nessa nossa base de dados.
Calma! Esses counts aleatórios vão fazer sentido quando usarmos o comando .explain().
Passo 4: Executar uma query usando explain()
Agora vamos mostrar o otimizador de consultas em ação. Para isso usaremos o comando explain e veremos qual plano de execução o mongo achou ideal nessa consulta:
db.pessoas.find({"endereco.cidade": "Porto Alegre"}).explain("executionStats")
Deixei marcados os itens importantes:
Documents returned: Quantos documentos a query “explicada” retornou, perceba que é o mesmo tamanho do count acima.
Documents examined: Quantos documentos a query precisou analisar pra conseguir entregar o resultado esperado para o usuário. Ou seja, usando o plano de execução o mongo precisou percorrer a collection inteira para ver quais pessoas moravam em Porto Alegre ou não.
STAGE COLLSCAN: Qual a estratégia dentro do plano de execução o mongo usou para chegar aos dados. Neste caso foi o COLLSCAN, que é uma abrevição para COLLECTION SCAN. Dessa forma, varreu toda a collection e comparou documento por documento para chegar no resultado da consulta.
Importante: O Stage utilizado é determinante para a performance da query.
Passo 5: Executar explain() apos a criação de um índice
Não é intuito do tutorial explicar cada stage e como eles impactam no tempo da consulta. Entretanto, mostrar que ao executarmos a simples ação de criarmos um índice para coluna que estamos consultando, o resultado muda completamente. O comando para criar um índice no Mongo é:
db.pessoas.createIndex({"endereco.cidade": 1})
O otimizador de consultas entende isso e passa a criar um novo plano ideal para a mesma consulta.
Perceba que o documents returned permanece igual (O QUE), porém o documents examined caiu consideravelmente, mostrando que o COMO o mongo chegou a esses dados é muito diferente da primeira consulta.
Além disso o STAGE trocou de COLLECTION_SCAN para IX_SCAN, evidenciando que o mongo agora está usando o índice. Parece uma diferença pequena, mais saiba que essa diferença pode ser absurda no tempo de respostas, basta imaginar o que aconteceria se a collection tivesse 1 milhão de registros.
Passo 6: Ver todos os planos de execução
Para concluir, é possível ver todos os planos de execução criado pelo banco mudando o comando explain para .explain(“allPlansExecution”):
db.pessoas.find({"endereco.cidade": "Porto Alegre"}).explain("allPlansExecution")
Obs: Se o array estiver vazio, indica que o plano vencedor foi o único plano criado.
Conclusão
A ideia desse artigo foi abordar o uso do comando explain() no MongoDB para explicar como ele chega ao resultado de uma consulta usando o otimizador de consultas para isso.
Também explicamos o que são os planos de execução e como visualizar eles através do explain. E por último, mostramos na prática como a criação de um índice pode mudar os planos de execução do banco.
Nos vemos no próximo tutorial, forte abraço!!