26 de outubro de 2026
Otimização de consultas em bancos de dados MongoDB
Matheus Barbosa Silva; Vinicius Santos Andrade
DOI: 10.22167/2675-6528-202601234
Artigo elaborado pela ferramenta ResumeAI, solução de inteligência artificial desenvolvida pelo Instituto Pecege voltada à síntese e redação.
Resumo
A otimização de consultas em bancos de dados MongoDB foi investigada, considerando a relevância do processamento de dados em larga escala no contexto NoSQL. O estudo objetivou avaliar, por meio de experimentos práticos, como a estrutura e a configuração de índices influenciam a performance de leitura e o custo operacional de escrita. A metodologia empregou pesquisa bibliográfica exploratória e pesquisa experimental quantitativa, que incluiu dez experimentos controlados utilizando bases de dados públicas, como a NYC 311 Service Requests. Os resultados demonstraram que índices de campo único reduziram o tempo médio de execução em quase 100% para atributos de alta cardinalidade, mas tiveram eficácia limitada em campos de baixa cardinalidade devido à seletividade. Verificou-se o impacto determinante da regra do prefixo em índices compostos e identificou-se o ponto de inflexão onde o custo de busca individual de documentos superou a varredura sequencial. Adicionalmente, observou-se que o acúmulo excessivo de índices elevou a latência de escrita e a contenção de recursos no motor de armazenamento. Concluiu-se que, embora os índices sejam fundamentais para a escalabilidade, eles demandam um planejamento criterioso da distribuição dos dados para prevenir que o mecanismo de seleção de planos do MongoDB adote estratégias subótimas.
Palavras-chave: Desempenho; Índices; NoSQL; Planos de Execução; Seletividade.
1. Introdução
Bancos de dados não-relacionais, frequentemente referidos como NoSQL, tornaram-se pilares essenciais em arquiteturas de aplicações modernas, especialmente aquelas que lidam com armazenamento e processamento de dados em larga escala. Cenários como redes sociais, aplicações de Internet das Coisas (IoT) e plataformas de consumo de mídias geram volumes massivos de dados que exigem sistemas capazes de gerenciar e consultar informações com alta demanda e volume. Nesse contexto, a eficiência na inserção e consulta de dados é crucial, pois impacta diretamente a experiência do usuário e a economia de recursos computacionais. O MongoDB, um sistema gerenciador de bancos de dados (SGBD) não-relacional popular, destaca-se por sua capacidade de escalabilidade horizontal e suporte a diversos tipos de índices, tornando-o um objeto de estudo relevante para a otimização de consultas em ambientes de Big Data (Chopade & Pachghare, 2020).
Uma das principais estratégias para otimizar o desempenho de consultas em bancos de dados não-relacionais, como o MongoDB, é a construção e o uso eficiente de índices. Índices são estruturas de dados que organizam logicamente um ou mais atributos de uma coleção, permitindo que o sistema localize rapidamente os documentos desejados sem a necessidade de varrer exaustivamente todos os registros. Essa capacidade de definir um escopo de busca mais restrito é fundamental para agilizar operações de leitura, especialmente em atributos com alta cardinalidade e que são frequentemente utilizados como filtros. A otimização de índices é um processo contínuo que visa maximizar a eficiência no uso de recursos computacionais, como memória e poder de processamento, conforme apontado por Marat et al. (2024).
Apesar dos benefícios evidentes na aceleração de consultas, a implementação e manutenção de índices não são operações isentas de custos e complexidades. A literatura técnica adverte para um trade-off significativo entre a performance de leitura e o custo operacional de escrita. Cada índice adicionado a uma coleção impõe um esforço adicional ao motor de armazenamento, exigindo manutenção síncrona da estrutura de árvore B+ a cada alteração nos dados (Chopade & Pachghare, 2020). Em ambientes com alta concorrência de escrita, o acúmulo excessivo de índices pode levar ao aumento da latência de escrita e à contenção de travas por recursos, impactando a performance global do sistema (Tao et al., 2024). Além disso, o mecanismo de seleção de planos de consulta do MongoDB, baseado em uma abordagem empírica conhecida como First Past The Post (FPTP), pode apresentar um viés que favorece planos de índice rápidos para os primeiros resultados, mas subótimos para consultas que retornam grandes volumes de dados, o que pode anular os ganhos de otimização esperados.
Para mitigar esses desafios e garantir uma otimização eficaz, é crucial compreender conceitos teóricos como a seletividade de consultas e o ponto de inflexão (tipping point). A seletividade refere-se à proporção de documentos retornados por uma consulta em relação ao total da coleção, e sua baixa incidência pode tornar o uso de índices menos vantajoso do que uma varredura sequencial completa (Tao et al., 2024). O ponto de inflexão demarca o limiar onde o custo de percorrer o índice e buscar individualmente cada documento supera o benefício da indexação. Adicionalmente, a regra do prefixo em índices compostos é determinante, pois o MongoDB utiliza esses índices apenas quando os campos do filtro formam um prefixo contínuo a partir do primeiro campo do índice (Chopade & Pachghare, 2020). A máxima eficiência é alcançada com consultas cobertas (covered queries), onde todos os campos de filtro e projeção estão contidos no índice, eliminando a necessidade de acesso aos documentos originais no disco e minimizando o custo computacional (Chopade & Pachghare, 2020; Tao et al., 2024).
Dada a complexidade e os múltiplos fatores que influenciam a otimização de consultas em bancos de dados NoSQL como o MongoDB, torna-se imperativo um estudo aprofundado sobre o impacto de diferentes tipos e configurações de índices. A compreensão desses mecanismos internos e a quantificação de seus efeitos permitem estruturar boas práticas e estender os benefícios da otimização para diversos contextos de aplicação. Assim, este estudo propõe-se a analisar a influência positiva e negativa na otimização de consultas de diversos tipos de índices disponíveis em bancos de dados MongoDB, com o objetivo geral de avaliar, por meio de experimentos práticos, como a estrutura e a configuração de índices influenciam a performance de leitura e o custo operacional de escrita.
2. Material e Métodos
A presente pesquisa foi desenvolvida por meio de uma abordagem metodológica mista, que integrou uma pesquisa bibliográfica exploratória com uma pesquisa experimental de natureza quantitativa. A etapa exploratória teve como propósito descrever e analisar o funcionamento e as particularidades dos diversos tipos de índices disponíveis no sistema gerenciador de bancos de dados MongoDB. Esta análise teórica forneceu a base para a formulação dos experimentos práticos, permitindo a verificação das características observadas em um ambiente controlado.
A pesquisa experimental foi delineada para validar e quantificar o potencial de otimização de diferentes tipos e configurações de índices sobre a performance de leitura e o custo operacional de escrita em instâncias do MongoDB. Para tal, foram conduzidos dez experimentos controlados, cada um concebido para isolar um conceito específico, como seletividade de consultas, cobertura de consultas ou o impacto no custo de escrita. Essa estrutura permitiu a comparação direta entre cenários com e sem o recurso de indexação sob investigação.
Os experimentos foram realizados em um ambiente local de instância única do MongoDB, configurado sem sharding ou replica sets, a fim de isolar o comportamento dos índices das variáveis inerentes a topologias distribuídas. O ambiente computacional consistiu em um Subsistema Windows para Linux (WSL2) com a distribuição Debian 10, operando sobre o sistema operacional Windows 11 de 64 bits. A versão 8.0 do MongoDB, a mais estável à época, foi utilizada.
O equipamento empregado para a execução dos experimentos possuía um processador AMD Ryzen 7 3700U (2,30GHz, 8 núcleos) e 20 GB de memória RAM, dos quais 17,9 GB estavam disponíveis para o sistema operacional. A escolha desse ambiente visou replicar um cenário de desenvolvimento e teste comum, garantindo a reprodutibilidade dos resultados obtidos.
Para a realização dos experimentos, foram empregadas duas bases de dados públicas. A principal foi o conjunto NYC 311 Service Requests, obtido do portal NYC Open Data (data.cityofnewyork.us). Este dataset, com mais de 20 milhões de registros de solicitações de serviços públicos desde 2020, apresentava campos de diversas naturezas, incluindo datas, coordenadas geográficas e categorias de alta e baixa cardinalidade.
Adicionalmente, para os experimentos que envolviam campos do tipo array, utilizou-se o conjunto sample_mflix, um dataset de amostra distribuído oficialmente pelo MongoDB. Este conjunto contém documentos de filmes com campos de lista, como gêneros e elenco. A importação de ambos os datasets para coleções MongoDB locais foi realizada por meio da ferramenta `mongoimport`.
O protocolo de medição foi uniforme em todos os experimentos. Cada consulta ou operação foi executada cinquenta vezes consecutivas sob condições idênticas, com exceção dos experimentos de escrita, para os quais o número de repetições foi trinta, devido à maior duração dessas operações. A métrica primária coletada foi o tempo de execução em milissegundos, obtida através do método `explain` do MongoDB, que reporta o tempo real de execução do motor de consultas, excluindo a latência de rede.
Métricas secundárias, como o número de documentos e chaves examinadas, foram registradas para auxiliar na interpretação do mecanismo interno de cada plano de execução. A escolha de cinquenta repetições por condição foi determinada pela necessidade de estabilizar a estimativa da média, considerando a variabilidade natural de execuções em ambiente local, onde fatores como o estado do cache do sistema operacional e a concorrência de processos podem introduzir flutuações.
Com esse volume de observações, foi possível construir intervalos de confiança de 95% para a média, utilizando a distribuição t de Student com n-1 graus de liberdade. Essa abordagem é adequada para amostras de tamanho finito sem a pressuposição de normalidade da população. O controle explícito do estado do cache de planos foi implementado em cada experimento para distinguir rigorosamente entre leituras frias e leituras quentes.
O mecanismo de seleção de planos do MongoDB, conhecido como First Past The Post (FPTP), foi um ponto central de investigação, conforme descrito por Tao et al. (2024). Este mecanismo adota uma abordagem empírica, onde múltiplos planos de execução são testados simultaneamente, e o “vencedor” é aquele que retorna os primeiros resultados mais rapidamente, o que pode introduzir um viés de preferência por varreduras de índice.
A compreensão da estrutura de árvore B+ utilizada pelos índices do MongoDB, que exige manutenção contínua e síncrona a cada alteração nos dados, foi fundamental para a análise do custo operacional de escrita (Chopade & Pachghare, 2020). Cada novo índice adicionado a uma coleção impõe um esforço adicional ao motor de armazenamento, impactando a latência de escrita e a contenção de travas em cenários de alta concorrência.
O processo de execução e coleta de dados dos experimentos seguiu um esquema que envolveu a importação de dados externos via `mongoimport` e `mongorestore` para uma instância local do MongoDB 8.0. Scripts Python 3.11, utilizando as bibliotecas `pymongo` e `scipy`, foram empregados para executar as consultas, coletar as métricas de `executionTimeMillis` e calcular os intervalos de confiança de 95%.
Os dados coletados foram organizados em arquivos CSV, e os resultados foram visualizados por meio de gráficos gerados com a biblioteca `matplotlib`. Ao todo, foram conduzidos dez experimentos. Os experimentos de 1 a 8 investigaram o impacto de diferentes tipos de índices na performance de leitura, abrangendo índices de campo único, compostos, de texto, geoespaciais e multichave, bem como os conceitos de seletividade, consultas cobertas e o comportamento do cache de planos.
Os experimentos 9 e 10 focaram nos custos associados à manutenção de índices, mensurando o impacto sobre as operações de escrita e avaliando a economia de armazenamento proporcionada por índices parciais. A análise dos dados foi realizada com base nas métricas de tempo de execução e no número de documentos e chaves examinadas, sem apresentar achados empíricos ou interpretações de resultados nesta seção.
3. Resultados e Discussão
A otimização de consultas em bancos de dados MongoDB é um tema central para aplicações que lidam com processamento e armazenamento de dados em larga escala, como observado em redes sociais e plataformas de IoT. Nesse contexto, a eficiência na leitura e escrita de dados é crucial, impactando diretamente a experiência do usuário e a economia de recursos computacionais. A utilização de índices surge como uma estratégia fundamental para agilizar operações de leitura, organizando logicamente atributos de uma coleção para permitir a localização rápida de documentos, conforme destacado por Chopade e Pachghare (2020).
Contudo, a implementação de índices não está isenta de custos e complexidades. Existe um trade-off significativo entre a performance de leitura e o custo operacional de escrita, pois cada índice adicionado exige manutenção síncrona da estrutura de árvore B+ a cada alteração nos dados (Chopade & Pachghare, 2020). Em ambientes com alta concorrência de escrita, o acúmulo excessivo de índices pode elevar a latência e a contenção de recursos, afetando a performance global do sistema (Tao et al., 2024). O mecanismo de seleção de planos de consulta do MongoDB, baseado na abordagem empírica First Past The Post (FPTP), pode ainda favorecer planos rápidos para os primeiros resultados, mas subótimos para consultas que retornam grandes volumes de dados, o que anula os ganhos esperados.
Para mitigar esses desafios, é essencial compreender conceitos como a seletividade de consultas e o ponto de inflexão. A seletividade indica a proporção de documentos retornados por uma consulta em relação ao total da coleção, e uma baixa seletividade pode tornar o uso de índices menos vantajoso que uma varredura sequencial (Tao et al., 2024). O ponto de inflexão demarca o limiar onde o custo de percorrer o índice e buscar individualmente cada documento supera o benefício da indexação. A regra do prefixo em índices compostos também é determinante, pois o MongoDB os utiliza apenas quando os campos do filtro formam um prefixo contínuo a partir do primeiro campo do índice (Chopade & Pachghare, 2020).
A máxima eficiência é alcançada com consultas cobertas (covered queries), nas quais todos os campos de filtro e projeção estão contidos no índice, eliminando a necessidade de acesso aos documentos originais no disco e minimizando o custo computacional (Chopade & Pachghare, 2020; Tao et al., 2024). Este estudo, portanto, analisou a influência de diversos tipos e configurações de índices em bancos de dados MongoDB, avaliando como a estrutura e a configuração de índices impactam a performance de leitura e o custo operacional de escrita por meio de experimentos práticos.
Tipos de índices
Para otimizar a performance de consultas, a escolha do tipo de índice adequado é fundamental, considerando a estrutura dos dados e os padrões de busca da aplicação. O MongoDB oferece uma variedade de índices que utilizam a estrutura de uma árvore B+ para localizar documentos eficientemente, evitando varreduras completas da coleção (collection scans), conforme descrito por Chopade e Pachghare (2020). Entre os principais tipos, o Índice de Campo Único é a forma mais básica, aplicada a um único campo, sendo essencial para buscas diretas e automaticamente criado para o identificador único da coleção (_id).
O Índice Composto engloba múltiplos campos em uma única estrutura para cobrir consultas com múltiplos filtros. A ordem dos campos é crucial, pois o MongoDB utiliza o índice apenas quando os campos do filtro formam um prefixo contínuo a partir do primeiro campo do índice. Já o Índice Multichave é projetado para indexar campos que contêm arrays, criando entradas de índice separadas para cada elemento do array, o que melhora a eficiência de buscas por elementos dentro de listas (Chopade & Pachghare, 2020).
Para aplicações que dependem de coordenadas geográficas, o Índice Geoespacial (tipos 2d e 2dsphere) otimiza consultas de proximidade e verificação de inclusão em áreas específicas. O Índice de Texto permite pesquisar termos dentro de strings considerando aspectos linguísticos, como radicais de palavras, e é sempre esparso, aplicando-se apenas a documentos com valores para os campos indexados. Por fim, o Índice Hashed, aplicado a um único campo, armazena o hash do valor indexado e é frequentemente utilizado em ambientes de sharding para garantir uma distribuição uniforme dos documentos entre os nós de um cluster.
Além desses, o MongoDB oferece Índices de Propriedades Especiais. Os Índices TTL (Time-To-Live) removem documentos mais antigos que um tempo especificado. Os Índices Únicos garantem a unicidade dos valores dos campos, impedindo a inserção de duplicidades. Os Índices Parciais restringem a aplicação de um índice a documentos que atendem a critérios específicos, sendo úteis para coleções grandes ou filtros específicos, pois ocupam menos espaço de armazenamento.
Mecanismo de seleção de planos e índices
O MongoDB adota uma abordagem empírica para a seleção de planos de execução, diferentemente de sistemas relacionais que usam estimativas estatísticas de custo. Esse mecanismo, descrito por Tao et al. (2024) como First Past The Post (FPTP), funciona como uma “corrida” entre diferentes estratégias de busca. Quando uma consulta é submetida e não há um plano válido em cache, o otimizador gera múltiplos planos candidatos, como o uso de diferentes índices ou uma varredura de coleção (collection scan).
Esses planos são executados simultaneamente em um sistema de escalonamento round-robin. O MongoDB declara como “vencedor” o plano que retornar uma quantidade predefinida de resultados (geralmente os primeiros 101 documentos) com o menor “trabalho” computacional, medido por unidades de esforço interno como leituras de disco ou entradas de índice percorridas. Uma vez selecionado, o plano é armazenado em um cache de planos de consulta (Query Plan Cache) para otimizar execuções futuras de consultas com estrutura lógica idêntica, evitando o custo da fase de teste.
No entanto, o cache de planos é invalidado em cenários específicos, como alterações significativas no volume de dados, criação ou remoção de índices, ou reinicialização do servidor. Tao et al. (2024) identificaram um “viés de preferência” do otimizador: como o critério de vitória se baseia nos primeiros resultados, o MongoDB tende a favorecer varreduras de índice (index scans) que encontram os primeiros registros rapidamente. Em consultas com baixa seletividade, o tempo total para percorrer um índice e buscar cada documento individualmente pode superar o tempo de uma varredura sequencial, demonstrando que a mera existência de um índice não garante a otimização global.
Análise do Trade-off entre performance de leitura e custo de escrita
A implementação de índices no MongoDB não é uma operação sem custos. Embora o foco principal seja a redução da latência em consultas, a literatura técnica adverte para o impacto colateral nas operações de modificação de dados. A eficiência das consultas é obtida por meio de uma estrutura de árvore B+ que exige manutenção contínua e síncrona a cada alteração no conjunto de dados, conforme detalhado por Chopade e Pachghare (2020).
Cada novo índice adicionado a uma coleção impõe um esforço adicional ao motor de armazenamento, como o WiredTiger. Ao inserir, remover ou atualizar um documento, o MongoDB não apenas modifica o dado bruto, mas também precisa atualizar de forma síncrona todas as entradas correspondentes nos índices associados. Em cenários de alta concorrência de escrita, o acúmulo de índices leva a dois efeitos colaterais principais: o aumento da latência de escrita e a contenção de travas (lock contention).
O aumento da latência de escrita ocorre porque o tempo necessário para completar uma operação de escrita possui uma relação de proporcionalidade direta com a quantidade de índices definidos na coleção (Chopade & Pachghare, 2020). O motor de armazenamento replica atualizações em cada estrutura e índice individualmente, resultando em maior tempo de resposta para operações de escrita. A contenção de travas, por sua vez, ocorre quando o motor de busca tenta atualizar simultaneamente diversas páginas de índices no cache de memória, gerando disputa por recursos entre diferentes threads de execução (Tao et al., 2024).
Mesmo com mecanismos modernos de gestão de concorrência, essa sobrecarga reduz a eficiência do processamento paralelo, podendo transformar o índice em um gargalo de desempenho sistêmico em ambientes de escrita intensiva. Outro fator crítico é que, para máxima eficiência, os índices devem residir na memória RAM, formando o resident set. Índices volumosos ou em excesso podem exceder a capacidade do cache, forçando o sistema a realizar operações de leitura e escrita diretamente no disco (swapping), o que degrada drasticamente a performance global (Tao et al., 2024).
O impacto do armazenamento físico também é relevante, pois índices complexos, como os multichave ou de texto, podem ocupar mais espaço em disco que os próprios documentos originais. A otimização no MongoDB, portanto, deve ser vista como um equilíbrio de recursos, ponderando o ganho de velocidade na leitura contra o custo de infraestrutura e a agilidade das operações de escrita. A análise de Chopade e Pachghare (2020) sugere que é preferível consolidar um índice composto que atenda a múltiplos padrões de consulta, respeitando a regra do prefixo, em vez de manter diversos índices individuais sobrepostos, minimizando o impacto nas escritas e otimizando o uso do cache de memória.
Manutenção de índices
A natureza dinâmica dos índices em bancos de dados MongoDB, que crescem e se alteram com a quantidade de dados armazenados, torna sua manutenção uma tarefa contínua. É essencial analisá-los e atualizá-los recorrentemente para que permaneçam relevantes diante das atualizações de esquemas e do crescimento da escala do banco de dados. Além disso, o espaço de armazenamento ocupado pelos índices deve ser considerado, pois índices grandes não apenas impactam o armazenamento, mas também a velocidade das operações de escrita, uma vez que atualizações de dados implicam em modificações nas estruturas indexadas (Marat et al., 2024).
O tamanho dos índices mantidos por um banco de dados é um fator crítico para o desempenho das operações, especialmente em contextos de grande escala, onde bancos de dados orientados a documentos são comumente aplicados. Assim, a definição dos índices deve acompanhar as atualizações do esquema de dados da base, garantindo que os benefícios proporcionados por essas estruturas não se percam. É desejável que o tamanho ocupado pelos índices seja justificado pelos seus casos de uso, de modo que a perda de desempenho em operações de escrita seja razoável e justificável dentro do contexto da aplicação.
Ponto de inflexão da seletividade de índices (Tipping point)
Um dos conceitos mais críticos na otimização de bancos de dados não-relacionais é a seletividade de consultas, que define a proporção de documentos retornados em relação ao total da coleção. Conforme discutido por Tao et al. (2024), existe um limiar teórico, denominado “ponto de inflexão” (tipping point), a partir do qual a utilização de um índice deixa de ser vantajosa e se torna mais onerosa do que uma varredura completa da coleção (collection scan).
A ineficiência de um índice em cenários de baixa seletividade, onde um grande volume de dados é retornado, justifica-se pelo custo operacional da fase de fetch. Quando o MongoDB utiliza um índice, ele primeiro percorre a árvore B+ para localizar as referências dos documentos e, em seguida, busca cada documento individualmente no disco ou no cache de memória. Se a consulta retornar uma porção considerável da coleção (acima de 30% ou 40%), o esforço de alternar repetidamente entre o índice e a camada de armazenamento supera o tempo de uma leitura sequencial completa.
Nesse estágio, o índice deixa de ser um atalho e se torna um intermediário oneroso e desnecessário, consumindo ciclos de CPU e operações de I/O adicionais. Tao et al. (2024) destacam um desafio adicional na otimização de consultas no MongoDB devido ao critério First Past The Post (FPTP), que possui um viés inerente a favor de planos baseados em índices. Como a “corrida” de planos termina assim que os primeiros resultados são encontrados, um plano de índice geralmente vence a competição por ser ágil na localização dos registros iniciais em sua estrutura de árvore B+.
Contudo, esse mecanismo de vitória precoce não contabiliza o trabalho total necessário para concluir a execução de uma consulta. Dessa forma, o otimizador pode fixar no cache um plano que é excelente para retornar os primeiros 100 documentos, mas que se torna drasticamente subótimo ao processar uma massa de dados volumosa. Para esta pesquisa, o ponto de inflexão serviu como a principal variável de teste no experimento prático, permitindo identificar situações onde a otimização pode requerer a remoção ou o preterimento de um índice em favor de uma varredura natural, visando diminuir o tempo de execução global.
Influência do cache de planos na execução de consultas
A análise de performance de consultas no MongoDB não pode ser dissociada do funcionamento do seu sistema de persistência de decisões, o Query Plan Cache. Conforme Tao et al. (2024), uma vez que o mecanismo FPTP elege um plano vencedor, este é armazenado em cache para otimizar futuras execuções de consultas com estrutura idêntica, ou seja, com o mesmo formato de filtros, desconsiderando-se os valores a serem filtrados. O benefício imediato do cache é a eliminação da fase de “corrida” de planos em cada nova consulta.
Entretanto, Tao et al. (2024) ressaltam que essa estabilidade pode ocultar uma desvantagem: se o plano armazenado no cache foi eleito sob condições específicas de volume de dados que mudaram drasticamente, o MongoDB continuará usando uma estratégia potencialmente subótima até que ocorra uma invalidação do cache. Para a metodologia desta pesquisa, essa característica demandou a distinção rigorosa entre leituras frias (cold reads), quando o cache está vazio e o motor é forçado a reavaliar os índices, e leituras quentes (warm reads), quando o plano já está consolidado e salvo em cache, refletindo a performance real do sistema estável.
Conforme detalhado por Chopade e Pachghare (2020), o cache de planos não é permanente, sendo invalidado automaticamente por eventos como a criação ou remoção de índices, reinicialização do servidor, ou quando o volume de documentos alterados na coleção atinge um limiar que sugere que as estatísticas de distribuição dos dados estão obsoletas. Portanto, para que os resultados e discussões deste trabalho fossem fidedignos, os experimentos práticos consideraram o estado do cache. Ignorar a existência do plano armazenado em cache poderia levar a conclusões errôneas, atribuindo a rapidez de uma consulta apenas à eficiência do índice, quando, na verdade, estaria sendo beneficiada pela ausência da fase de competição de planos em leituras quentes. Essa compreensão permitiu isolar o ganho real de performance proporcionado pela indexação em relação ao comportamento adaptativo próprio do otimizador de planos de consultas do MongoDB.
Otimização máximo por meio de consultas cobertas
No limite superior da eficiência de indexação encontra-se o conceito de consultas cobertas (covered queries). Segundo Chopade e Pachghare (2020), uma consulta é considerada “coberta” quando todos os campos solicitados na projeção da busca, bem como os critérios de filtro, fazem parte do mesmo índice composto. A relevância técnica desse tipo de consulta reside na eliminação completa da fase de fetch, que é a busca do documento original no disco ou cache de dados, o que a torna mais rápida. Como o índice que reside na RAM já contém todas as informações necessárias para retornar uma resposta ao cliente, o motor de busca pode evitar o acesso ao armazenamento de dados brutos.
Conforme discutido por Tao et al. (2024) ao analisar as unidades de trabalho, o custo de uma consulta coberta é drasticamente menor, pois reduz a utilização de I/O e minimiza a latência de rede e processamento. Para o experimento prático deste trabalho, a identificação de consultas cobertas foi feita por meio da métrica “totalDocsExamined: 0” na explicação da execução de uma consulta. Encontrar e apontar cenários de consultas cobertas permitiu demonstrar que a otimização de índices não visa apenas localizar documentos mais rapidamente, mas também eliminar integralmente a necessidade de acessá-los, num cenário ideal que permite alcançar o maior nível de otimização possível proporcionado por um índice em bancos de dados MongoDB.
Ambiente e configuração dos experimentos
A etapa experimental desta pesquisa teve como objetivo medir o impacto de diferentes tipos de índices e configurações sobre a performance de leitura e escrita em bancos de dados MongoDB. Para garantir a reprodutibilidade dos resultados e a validade estatística dos intervalos de confiança, o ambiente computacional, a base de dados e o protocolo de medição foram precisamente descritos. Os experimentos foram conduzidos em um ambiente local de instância única do MongoDB, versão 8.0, executado no Subsistema Windows para Linux (WSL2) com distribuição Debian 10, sobre um sistema operacional Windows 11 de 64 bits.
O equipamento utilizado possuía um processador AMD Ryzen 7 3700U (2,30GHz, 8 núcleos) e 20GB de memória RAM, com 17,9GB disponíveis para o sistema operacional. A base de dados principal foi o conjunto NYC 311 Service Requests, do portal NYC Open Data (data.cityofnewyork.us), contendo mais de 20 milhões de registros de solicitações de serviços públicos desde 2020. Este conjunto, com campos de diversas naturezas (datas, coordenadas, categorias de alta e baixa cardinalidade, texto livre), foi adequado para a aplicação de uma ampla gama de tipos de índices.
Para experimentos que demandavam campos do tipo array, utilizou-se o conjunto sample_mflix, um dataset de amostra distribuído oficialmente pelo MongoDB, contendo documentos de filmes com campos de lista como gêneros e elenco. Em ambos os casos, os dados foram importados para coleções MongoDB locais utilizando a ferramenta mongoimport. O protocolo de medição adotado em todos os experimentos foi uniforme: cada consulta ou operação foi executada cinquenta vezes consecutivas sob as mesmas condições, exceto para experimentos de escrita, que tiveram trinta repetições devido à maior duração.
O tempo médio de execução foi registrado em milissegundos, obtido por meio da métrica `executionTimeMillis` do método `explain` do MongoDB. Esse volume de repetições permitiu a construção de intervalos de confiança de 95% para cada medição, conforme estabelecido na Metodologia. Adicionalmente, o estado do cache de planos foi controlado explicitamente em cada experimento para garantir a distinção rigorosa entre leituras frias e quentes, onde essa distinção era relevante. Foram conduzidos dez experimentos, investigando o impacto de índices de campo único, compostos, de texto, geoespaciais e multichave, além dos conceitos de seletividade, consultas cobertas, comportamento do cache de planos e custos de manutenção de índices.
Experimento 1: Collection scan vs. índice de campo único
O primeiro experimento estabeleceu o impacto fundamental da indexação, medindo o tempo médio de execução de 50 consultas idênticas sobre a coleção NYC 311. Foram comparadas duas condições: com e sem índice de campo único, aplicadas a campos de cardinalidades distintas: `borough` (baixa cardinalidade, com 5 valores) e `created_date` (alta cardinalidade). O cache de planos foi limpo antes de cada série para isolar o efeito do índice da influência do cache.
Para o campo `created_date`, o ganho de performance foi expressivo: o tempo médio de execução caiu de 6.658 ± 90 ms (COLLSCAN) para 0,1 ms com o índice, representando uma redução de quase 100%. Esse resultado demonstra o comportamento esperado de um índice sobre um campo de alta cardinalidade, onde a organização das entradas em uma estrutura de árvore B+ permite ao MongoDB localizar o documento alvo diretamente, eliminando a necessidade de varrer os mais de 5 milhões de registros da coleção.
Para o campo `borough`, de baixa cardinalidade, o cenário foi diferente. O índice reduziu o tempo médio de 10.280 ± 1.110 ms para 5.677 ± 107 ms, uma redução de 44,8%. No entanto, o ganho foi substancialmente menor do que no campo de alta cardinalidade. Conforme discutido por Chopade e Pachghare (2020), a eficácia de um índice está diretamente associada à cardinalidade do campo indexado: campos com poucos valores distintos possuem baixa seletividade, o que significa que cada entrada do índice ainda aponta para um grande número de documentos da coleção. No conjunto de dados examinado, um dos valores possíveis para o campo `borough` está associado a mais de 1 milhão de documentos, o que impõe um custo elevado de fetch individual de cada registro. Esse comportamento antecipa o conceito de ponto de inflexão, explorado em detalhes no experimento 3.
Experimento 2: Regra do prefixo em índices compostos
O experimento 2 mediu o impacto da ordem dos campos de um índice composto sobre a performance de consultas com diferentes combinações de filtros. Foram criados dois índices: BCD ({borough, complaint_type, created_date}) e DCB ({created_date, complaint_type, borough}). Foram executadas 50 consultas por cenário com variações dos campos presentes no filtro da busca em relação ao prefixo de cada índice.
Os resultados confirmaram a regra do prefixo descrita por Chopade e Pachghare (2020): o MongoDB utiliza um índice composto apenas quando os campos do filtro formam um prefixo contínuo a partir do primeiro campo do índice. Consultas que respeitaram o prefixo do índice BCD obtiveram tempos progressivamente menores conforme mais campos foram adicionados ao filtro. Por exemplo, a consulta com apenas `borough` levou 43.890 ms, enquanto com um filtro adicional de `complaint_type` o tempo caiu para 0,3 ms, devido ao menor número de documentos retornados.
Consultas que violaram a regra do prefixo, como filtrar somente por `complaint_type` sem incluir `borough`, resultaram em COLLSCAN com 6912 ms, confirmando que o índice foi inteiramente ignorado pelo otimizador. Um resultado que merece atenção é o tempo de 43.890 ms para a consulta com prefixo `borough`. Embora o índice BCD tenha sido corretamente utilizado (IXSCAN), o filtro `borough` tem baixa seletividade, o que obrigou o motor de busca a percorrer mais de 1 milhão de entradas do índice e, em seguida, buscar cada um dos documentos correspondentes individualmente no disco no estágio de fetch. Esse custo acumulado tornou a operação com o índice 4.3 vezes mais lenta do que uma varredura sequencial direta.
Esse fenômeno é precisamente o viés descrito por Tao et al. (2024) no mecanismo FPTP: o otimizador elegeu o IXSCAN por este retornar os primeiros 101 documentos rapidamente durante a fase de corrida de planos, sem contabilizar o custo total da execução completa. Esse resultado reforça que a existência de um índice e o respeito à regra do prefixo nem sempre são suficientes para garantir eficiência; a seletividade do campo é determinante. Por fim, o índice DCB ignorou completamente a consulta por `borough` (COLLSCAN, 8.312 ms), mas a atendeu em 0,0 ms quando os três campos foram fornecidos com o prefixo correto. Isso demonstra que a ordem dos campos no índice deve refletir os padrões reais de consulta da aplicação, e que um índice mal ordenado pode ser completamente inútil para os casos de uso mais frequentes.
Experimento 3: Ponto de inflexão da seletividade
O experimento 3 investigou o ponto de inflexão (tipping point) descrito por Tao et al. (2024), que é o limiar de seletividade a partir do qual o uso de um índice deixa de ser vantajoso em relação a uma varredura sequencial da coleção. Para isso, foram selecionados oito tipos de reclamação da base NYC 311 com seletividades variando de 0,25% a 15,38% da coleção. Para cada um, foram realizadas 50 execuções em três condições: decisão livre do otimizador, IXSCAN forçado (via hint) e COLLSCAN forçado (via $natural).
Em baixas seletividades, o IXSCAN foi expressivamente superior: para 0,25% de seletividade, o IXSCAN completou a consulta em 40 ± 2 ms, contra 7.206 ± 244 ms do COLLSCAN, uma diferença de 180 vezes. À medida que a seletividade aumentou, a vantagem do IXSCAN diminuiu progressivamente. A partir de aproximadamente 2% de seletividade, os tempos do IXSCAN e do COLLSCAN passaram a convergir, mas ainda com vantagem para a execução com o índice.
O mecanismo subjacente revelou que o número de documentos examinados pelo IXSCAN cresce linearmente com a seletividade, enquanto o COLLSCAN sempre examina o total da coleção (mais de 5 milhões de documentos), independentemente do filtro. O cruzamento das duas curvas de tempo ocorre precisamente no intervalo em que o custo acumulado de percorrer a árvore B+ e buscar individualmente cada documento no disco supera o custo de uma leitura sequencial. Este achado reforça a importância de considerar a seletividade para a decisão de indexação.
Experimento 4: Consultas cobertas
O experimento 4 avaliou o impacto da eliminação do estágio de fetch por meio de consultas cobertas (covered queries). Após garantir o estado limpo da coleção (sem índices), foram medidas 50 execuções em três condições para a consulta {borough: “MANHATTAN”, complaint_type: “Noise – Residential”}: sem índice (COLLSCAN), com índice composto {borough, complaint_type, created_date} e projeção não coberta (IXSCAN + FETCH), e com o mesmo índice e projeção coberta (campos retornados restritos aos presentes no índice).
A condição COLLSCAN completou a consulta em 9.968 ms, varrendo todos os 5 milhões de documentos da coleção. Com índice e uma projeção não coberta, o tempo caiu para 3.338 ms, o que representa uma redução significativa, embora o motor de busca ainda precisasse acessar individualmente os documentos que satisfaziam o filtro no estágio de fetch para obter os valores dos campos não cobertos. A consulta coberta, por sua vez, completou a execução em apenas 119 ms, o que representa uma redução de 96% em relação ao IXSCAN com fetch.
O mecanismo responsável por essa diferença foi confirmado pela métrica `totalDocsExamined`, que foi zero para a consulta coberta, enquanto na consulta com IXSCAN e campos não cobertos no índice foi necessário examinar 88.627 documentos para obter os valores dos campos não cobertos. Conforme exposto por Chopade e Pachghare (2020), uma consulta coberta é aquela cujos campos de filtro e de projeção estão integralmente presentes no mesmo índice composto, permitindo ao motor de busca responder diretamente à busca sem precisar de acesso adicional ao armazenamento de documentos. Tao et al. (2024) identificam esse cenário como o de menor custo possível em termos de unidades de trabalho, eliminando completamente as operações de I/O associadas à recuperação dos dados brutos. Portanto, a identificação de cenários propícios a consultas cobertas constitui a forma mais eficiente de otimização por indexação disponível no MongoDB.
Experimento 5: Cache de planos – leituras frias vs. quentes
O experimento 5 isolou o impacto do cache de planos no tempo de execução de consultas, diferenciando três séries de 50 execuções: leituras frias (cache limpo antes de cada execução), leituras quentes (cache limpo uma vez antes da série completa) e uma série pós-invalidação (cache aquecido deliberadamente invalidado pela criação de um novo índice). A curva das leituras frias evidenciou um pico expressivo nas primeiras execuções: a primeira consulta levou 7.476 ms, o dobro do valor estabilizado, seguida de uma queda abrupta até o patamar de aproximadamente 3.600 ms a partir da terceira execução.
Esse pico inicial corresponde à fase de “corrida” de planos do mecanismo FPTP descrito por Tao et al. (2024): sem um plano válido em cache, o otimizador executa múltiplos candidatos em paralelo para eleger o vencedor, o que impõe um custo adicional em comparação às execuções seguintes, dado que nas próximas consultas o plano já está armazenado. Após o efeito de warmup do cache, as distribuições das três séries foram estatisticamente semelhantes, com sobreposição dos intervalos de confiança, indicando que, uma vez estabilizado o cache, seu estado não altera materialmente o tempo de execução médio para esta consulta específica.
O resultado relevante é o comportamento transiente, onde aplicações que realizam consultas pouco frequentes sobre uma dada coleção, ou que invalidam o cache constantemente, são afetadas pelo custo da fase de seleção de planos. Isso sublinha a importância de considerar o ciclo de vida do cache e a frequência das consultas ao projetar estratégias de indexação, especialmente em ambientes de produção com cargas de trabalho variáveis.
Experimento 6: Índice de texto vs. expressão regular
O experimento 6 comparou duas abordagens para buscas textuais na coleção NYC 311: o operador `$regex` com varredura sequencial e o operador `$text` com índice de texto. Foram realizadas 50 execuções por condição sobre o campo `resolution_description`, que contém descrições textuais. Os resultados demonstraram uma diferença de performance expressiva. A busca com `$regex` realizou um COLLSCAN em todos os mais de 5 milhões de documentos e completou cada execução em 15.590 ± 185 ms.
Em contraste, a busca com `$text` utilizou o índice e completou a mesma busca em 730 ± 30 ms, sendo 21 vezes mais rápida. Já a busca com exclusão de termos, que demanda avaliação adicional do motor textual para filtrar os resultados, levou 1.791 ms ± 47 ms com o índice, ainda assim 8,7 vezes mais rápida que o `$regex`. Esses resultados evidenciam a superioridade do índice de texto para buscas textuais complexas.
Contudo, essa otimização tem um custo de armazenamento significativo: o índice de texto ocupou 656.3MB em disco, o que é aproximadamente 12 vezes maior que o tamanho do índice padrão (_id). Esse custo de armazenamento pode, em alguns casos, superar até o tamanho dos próprios documentos e deve ser considerado na decisão de criação do índice. Para este experimento, o índice de texto para um único campo ocupou cerca de 13% do espaço total consumido pela coleção, com mais de 5 milhões de documentos.
Experimento 7: Índice geoespacial
O experimento 7 avaliou a performance de consultas geoespaciais com o índice 2dsphere do MongoDB. Para isso, o campo `location_geo` foi criado nos documentos da coleção NYC 311 no formato GeoJSON. Foram realizadas 50 execuções por condição com o operador `$near` para buscas com raios variando entre 500m e 10km, além de uma série com o operador `$geoWithin` aplicado a um polígono correspondente à ilha de Manhattan.
O resultado mais imediato foi que a operação `$near` executou todas as buscas por diferentes raios em tempos médios estatisticamente equivalentes, variando de 17,5 ± 4,1 ms para 500 m a 15,9 ± 0,7 ms para 10 km, sem crescimento perceptível em função do aumento do raio. Esse comportamento é explicado pelo limite de 1000 documentos retornados configurado no experimento: independentemente do raio, o índice retorna os 1000 documentos mais próximos do ponto de referência buscado, de modo que o custo permaneceu constante, demonstrando a eficiência do índice 2dsphere para consultas de proximidade com retorno limitado.
Em contraste, o operador `$geoWithin` com o polígono de Manhattan apresentou comportamento oposto: sem limitação de documentos retornados, o motor de busca precisou examinar todos os 1.206.246 registros com coordenadas dentro do polígono, resultando em um tempo médio de 51.797 ± 377 ms. Esse contraste evidencia que a utilização de `$geoWithin` deve ser acompanhada de filtros adicionais ou limites explícitos de retorno em aplicações que exigem baixa latência, pois a busca em grandes áreas, mesmo com um índice, pode ser onerosa.
Experimento 8: Índice multichave
O experimento 8 avaliou a eficácia do índice multichave (multikey index) para buscas em campos do tipo array. Utilizou-se a coleção `sample_mflix.movies` com o campo `genres`, que armazena um array com um a cinco gêneros por filme. Foram realizadas 50 execuções para cada gênero com e sem um índice, além de consultas com o operador `$all` buscando por múltiplos elementos simultaneamente.
O ganho proporcionado pelo índice multichave foi fortemente dependente da seletividade do gênero buscado. Para “Western” (1,1% da coleção, 242 filmes), o índice reduziu o tempo de 20 ms para 3 ms. Já para “Romance” (15,5% da coleção, 3318 filmes), a operação foi de 19,4 ms para 6,3 ms. Esse padrão confirma que o índice multichave opera sob os mesmos princípios do índice de campo único: cria entradas individuais para cada campo do array, tal que quanto menor a proporção de documentos cobertos, maior é o benefício provido pelo índice em relação à varredura sequencial.
O limite do índice multichave manifestou-se nos gêneros de alta seletividade. Para “Drama” (58% da coleção, 12.385 filmes), o índice resultou em tempo médio de 20,5 ms, ligeiramente superior aos 18,6 ms sem índice. Esse resultado reforça o conceito de ponto de inflexão observado no experimento 3, agora em escala reduzida: para retornar muitos documentos, um COLLSCAN pode ser mais vantajoso que um IXSCAN + fetch em alguns cenários, indicando que a seletividade é um fator crítico mesmo para índices multichave.
Experimento 9: Impacto dos índices na performance de escrita
O experimento 9 mediu a relação entre o número de índices mantidos por uma coleção e a latência de operações de escrita. Para isolar esse efeito, utilizou-se uma coleção temporária com documentos sintéticos no formato de NYC 311. Foram testados cenários com 0 a 5 índices adicionais ao `_id` padrão, e para cada cenário realizaram-se 30 inserções em lote de 1000 documentos (insertMany) e 30 operações de atualização em massa (updateMany).
Os resultados confirmaram a relação de proporcionalidade direta entre número de índices e latência de escrita descrita por Chopade e Pachghare (2020). Para operações de inserção, o tempo médio cresceu de 19,4 ± 1 ms (apenas com o índice `_id`) para 32,3 ± 2,9 ms com 5 índices adicionais, um overhead acumulado de 66%. A tendência linear foi visível ao longo de toda a curva. Para atualizações em massa, o padrão foi semelhante: de 135,7 ± 20,3 ms sem índices adicionais para 207,6 ± 50,1 ms com cinco índices, um overhead de 53%.
A tendência observada justifica a recomendação de Chopade e Pachghare (2020) de consolidar índices compostos que atendam a múltiplos padrões de consulta em vez de manter índices individuais sobrepostos. Essa abordagem visa minimizar o impacto sobre as operações de escrita sem comprometer o desempenho da leitura, otimizando o uso de recursos do servidor e garantindo a escalabilidade do sistema.
Experimento 10: Índice parcial
O experimento 10 avaliou o índice parcial, que restringe sua cobertura a um subconjunto de documentos da coleção de acordo com uma expressão de filtro. Comparou-se um índice completo em `complaint_type` e um índice parcial no mesmo campo com um filtro para abranger apenas documentos com status “Open”, medindo-se tanto a performance de consultas quanto o espaço de armazenamento ocupado por cada índice.
O tamanho do índice parcial ocupou apenas 0,42 MB contra 26,46 MB do índice completo, representando uma economia de 98,4%. Essa proporção reflete diretamente a fração de documentos que atendem ao filtro de status na coleção (apenas 1,4% do total), pois o índice parcial cria entradas apenas para documentos que satisfazem o filtro. No painel de performance, o comportamento do índice parcial revelou-se preciso na cobertura de seu escopo. Para a consulta de status “Open” com um filtro que não deve retornar nenhum documento, o índice parcial completou a execução em aproximadamente 0 ms com nenhum documento examinado.
A mesma busca sem um índice (COLLSCAN) precisou de 11.461 ± 1.329 ms para chegar à mesma conclusão após varrer mais de 5 milhões de documentos. Para uma consulta não coberta pelo índice, ou seja, uma busca por status “Closed”, os resultados com e sem índice foram equivalentes, como esperado, dado que o índice não foi aplicado por não atender ao filtro. Esse conjunto de resultados demonstra que o índice parcial é uma ferramenta de otimização altamente específica: proporciona ganho máximo para o subconjunto de documentos cobertos, não cria custo de manutenção de operações de escrita fora do filtro e ocupa uma fração menor do armazenamento em comparação ao índice completo.
Em síntese, os experimentos demonstraram que a eficácia da indexação em MongoDB está intrinsecamente ligada à cardinalidade dos atributos, à correta aplicação da regra do prefixo em índices compostos e à seletividade das consultas, que define o ponto de inflexão onde o benefício do índice pode ser superado pelo custo de varredura sequencial. As consultas cobertas representam o ápice da otimização, eliminando o acesso ao disco, enquanto o cache de planos influencia o desempenho transiente. Contudo, o acúmulo de índices eleva a latência de escrita e a contenção de recursos, e índices específicos como os de texto e geoespaciais, embora poderosos, demandam consideração de seus custos de armazenamento e limites de aplicação. O planejamento criterioso da distribuição dos dados e da configuração dos índices é, portanto, fundamental para prevenir estratégias subótimas do mecanismo de seleção de planos do MongoDB e garantir a escalabilidade.
4. Conclusão
Este estudo avaliou a influência da estrutura e configuração de índices na performance de leitura e no custo operacional de escrita em bancos de dados MongoDB. Verificou-se que índices de campo único proporcionaram reduções de tempo de execução de quase 100% para atributos de alta cardinalidade, embora sua eficácia fosse limitada em campos de baixa cardinalidade devido à seletividade. A regra do prefixo em índices compostos demonstrou ser determinante, e identificou-se um ponto de inflexão onde o custo de busca individual de documentos superava a varredura sequencial. Adicionalmente, observou-se que consultas cobertas representam o ápice da otimização, eliminando a necessidade de acesso ao disco. Os experimentos também revelaram o viés do mecanismo First Past The Post (FPTP) do MongoDB, que, ao priorizar os primeiros resultados, pode fixar planos subótimos para consultas que retornam grandes volumes de dados.
Em contrapartida, o acúmulo excessivo de índices elevou a latência de escrita e a contenção de recursos no motor de armazenamento, evidenciando um trade-off crucial. Índices específicos, como os de texto e geoespaciais, mostraram-se poderosos para suas finalidades, mas com custos de armazenamento significativos, enquanto índices parciais ofereceram otimização direcionada e economia de espaço. A principal contribuição deste trabalho reside em fornecer uma base sólida para a tomada de decisão em arquiteturas de dados de alto volume, onde a otimização de consultas é estratégica. Recomenda-se, portanto, um planejamento criterioso da distribuição dos dados e da configuração dos índices para mitigar os riscos de estratégias subótimas e garantir a escalabilidade do sistema.
Referências Bibliográficas
Chopade, R.; Pachghare, V. 2020. MongoDB Indexing for Performance Improvement. In: ICT Systems and Sustainability, 2020, Goa, Índia. Anais… p. 529-539.
data.cityofnewyork.us [Referência completa não encontrada no documento original]
Marat et al., 2024 [Referência completa não encontrada no documento original]
Tao, D.; Enqi, L.; Kadupitige, S. R.; Cahill, M.; Fekete, A.; Röhm, U. 2024. First past the post: evaluating query optimization in MongoDB. In: Australian Database Conference, 2024, Gold Coast, Australia. Anais… p. 99-113.
Artigo oriundo de Trabalho de Conclusão de Curso da Especialização em Engenharia de Software do MBA USP/Esalq
Para saber mais sobre o curso, clique aqui e acesse a plataforma MBX Academy

