Quando você cria um índice no SQL Server, no PostgreSQL ou no MongoDB e a consulta que levava segundos passa a responder em milissegundos, quase sempre há a mesma estrutura de dados trabalhando nos bastidores: a árvore B (mais precisamente, a variante B+). Entender como ela funciona é o que separa quem cria índices no chute de quem sabe por que um índice acelera as leituras, mas cobra um preço nas escritas. Vamos abrir essa caixa-preta.
A árvore por trás de quase todo índice
Um índice B-tree organiza os dados em camadas: uma raiz no topo, nós intermediários (galhos) no meio e as folhas embaixo, onde ficam as entradas ordenadas do índice. Cada nó de galho guarda o maior valor do nó filho correspondente, o que permite navegar de cima para baixo comparando o valor buscado até chegar à folha certa.
O detalhe elegante, como explica o clássico Use The Index, Luke!, é que as folhas não ficam necessariamente em ordem física no disco: elas são conectadas por uma lista duplamente encadeada que garante a ordem lógica. É como uma lista telefônica embaralhada em que cada página aponta para a próxima e para a anterior.
Por que a busca fica tão rápida
A força da árvore B é a profundidade logarítmica. Como cada nó empacota centenas de entradas, cada nível adicional multiplica a capacidade por cerca de 100. Na prática, índices com milhões de registros costumam ter apenas quatro ou cinco níveis de altura.
Localizar uma linha específica entre milhões de registros custa só quatro ou cinco saltos na árvore. É por isso que um índice bem escolhido transforma varreduras inteiras em buscas quase instantâneas.
O outro lado da moeda: escrever custa caro
A ordenação que torna a leitura veloz é justamente o que encarece a escrita. Quando uma página de índice enche e uma nova linha precisa entrar no meio dela, o banco faz um page split: aloca uma página nova, redistribui os dados entre as duas e ainda atualiza o galho acima e os ponteiros da lista encadeada.
Brent Ozar mostra bem essa amplificação de escrita: em seu exemplo, a atualização de uma única linha gerou quatro operações de escrita, porque o split precisou tocar a página nova, o nó intermediário e o ponteiro da página vizinha. O efeito colateral é a fragmentação: folhas espalhadas pelo disco transformam leituras sequenciais em acessos aleatórios.
Fill factor ainda vale a pena?
Por anos a receita foi deixar espaço livre nas páginas (um fill factor abaixo de 100%) para acomodar inserções sem provocar splits. Ozar defende que essa herança faz cada vez menos sentido no hardware atual: page splits pesavam quando discos magnéticos entregavam cerca de 150 operações de E/S por segundo, mas SSDs e NVMe já falam em centenas de milhares a milhões de IOPS.
- Para chaves sequenciais (colunas identity ou autoincremento), as inserções acontecem sempre no fim do índice, e os splits praticamente somem: mantenha o fill factor em 100%.
- Evite manutenções agressivas de reorganização de índice só para combater fragmentação que o armazenamento moderno absorve sem esforço.
- Desconfie do contador Page Splits/sec: ele mistura splits reais com alocações normais de página e pode assustar sem motivo.
Concorrência: o gargalo invisível dos bloqueios
Ter a estrutura certa não basta quando dezenas de threads disputam as mesmas páginas. Historicamente, os motores protegiam cada página da árvore com bloqueios (locks), e aí mora um custo escondido. Em novembro de 2025, a engenharia do MongoDB publicou como reescreveu a B-tree do WiredTiger para eliminar esses bloqueios.
O diagnóstico foi revelador: numa consulta simples que percorre três níveis da árvore (raiz, intermediário e folha), as três aquisições e liberações de lock respondiam por cerca de um terço de todo o custo da consulta.
Um terço do trabalho de uma busca ia embora só em administrar bloqueios, sem ler um único dado útil.
A solução combina duas técnicas sem lock. Para leituras, hazard pointers: cada thread publica um ponteiro ao passar por uma página, e o gerenciador de cache só remove uma página depois de conferir que ninguém a está usando. Para escritas, skip lists atualizadas de forma atômica com Compare-And-Swap, em que apenas o nível base precisa ter sucesso e os níveis superiores são melhor-esforço.
Os números que a MongoDB divulgou mostram o tamanho do gargalo removido: cerca de 47% mais throughput em leitura pura (de 1,7 para 2,5 milhões de operações por segundo em um teste com 64 threads) e ganhos de até quatro vezes em escrita sob alta contenção, com 128 threads inserindo ao mesmo tempo.
O que levar para o seu projeto
Três motores diferentes, uma mesma base conceitual e lições que se conversam:
- Prefira chaves de inserção crescente em índices que recebem muita escrita: elas concentram as inserções no fim da árvore e minimizam splits e fragmentação.
- Escolha índices pela seletividade e pela ordem das colunas, não pela quantidade: cada índice extra é uma árvore a mais para manter a cada escrita.
- Nem todo gargalo é o disco. Em cargas muito concorrentes, contenção e bloqueios podem pesar tanto quanto a E/S, como o próprio motor do MongoDB deixou explícito.
No fim, indexar bem é equilibrar leitura e escrita com consciência do que acontece na árvore. Quem enxerga a estrutura por baixo do CREATE INDEX toma decisões melhores em qualquer um dos três bancos.
Fontes