Ir para o conteúdo

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!!

Outras publicações