Índices: o que são e por que mudam tudo

1. O problema que os índices resolvem

Imagine que você precisa encontrar uma palavra específica em um livro de 1000 páginas. Sem um sumário ou índice remissivo, você seria obrigado a folhear cada página até encontrar o termo desejado. Esse processo é lento, ineficiente e se torna inviável conforme o livro cresce.

Em bancos de dados, o cenário é análogo. Quando uma consulta é executada sem um índice adequado, o SGBD realiza uma varredura completa da tabela (Full Table Scan). Isso significa que cada linha da tabela é lida sequencialmente até que os registros correspondentes sejam encontrados.

Considere uma tabela clientes com 10 milhões de registros:

-- Consulta sem índice
SELECT * FROM clientes WHERE email = 'maria@exemplo.com';

Sem um índice na coluna email, o banco precisará ler todas as 10 milhões de linhas para encontrar o registro desejado. Em disco, isso pode significar a leitura de centenas de milhares de páginas de dados, resultando em uma consulta que leva segundos ou até minutos.

Com um índice apropriado, a mesma consulta pode ser resolvida lendo apenas algumas páginas do índice, reduzindo o tempo para milissegundos.

2. O que é um índice no banco de dados?

Um índice é uma estrutura de dados auxiliar que acelera a recuperação de registros em uma tabela. A estrutura mais comum é a B-Tree (árvore balanceada), que armazena pares de (valor indexado, ponteiro para a linha) organizados de forma hierárquica.

A criação de um índice é simples:

CREATE INDEX idx_clientes_email ON clientes(email);

Após essa instrução, o banco mantém uma estrutura separada que mapeia cada valor de email para sua localização física na tabela. Quando uma consulta filtra por email, o banco percorre a B-Tree em vez de escanear a tabela inteira.

3. Tipos de índice mais comuns

Índice B-Tree

Padrão na maioria dos SGBDs (PostgreSQL, MySQL, Oracle, SQL Server). Excelente para consultas de igualdade, intervalo e ordenação.

CREATE INDEX idx_nome ON clientes(nome);

Índice Hash

Otimizado para buscas por igualdade exata. Não suporta ordenação ou buscas por intervalo.

CREATE INDEX idx_hash_email ON clientes USING HASH (email);

Índice Bitmap

Ideal para colunas com poucos valores distintos (baixa cardinalidade), como sexo ou status. Muito usado em Data Warehousing.

CREATE BITMAP INDEX idx_bitmap_status ON pedidos(status);

Índice Clusterizado

Organiza fisicamente os dados da tabela na ordem do índice. Cada tabela pode ter apenas um índice clusterizado.

-- SQL Server
CREATE CLUSTERED INDEX idx_id ON clientes(id);

4. Como o banco utiliza o índice em uma consulta

Quando uma consulta é executada, o otimizador de consultas decide se usa ou não um índice disponível. As duas estratégias principais são:

  • Index Scan: percorre todas as folhas do índice (útil quando muitos registros são selecionados)
  • Index Seek: navega diretamente pela árvore até o valor desejado (busca pontual)

Exemplo prático:

-- Consulta que usará Index Seek (se o índice existir)
SELECT * FROM clientes WHERE email = 'maria@exemplo.com';

-- Plano de execução (EXPLAIN)
EXPLAIN SELECT * FROM clientes WHERE email = 'maria@exemplo.com';

A diferença de custo é drástica:
- Sem índice: leitura de 10 milhões de linhas (suponha 100.000 páginas de disco)
- Com índice: leitura de 3-4 níveis da B-Tree + 1 página de dados (cerca de 4-5 páginas)

5. Cuidados e custos dos índices

Índices não são gratuitos. Cada índice adiciona custos em operações de escrita:

-- INSERT: precisa atualizar a tabela + todos os índices
INSERT INTO clientes (id, nome, email) VALUES (1001, 'João', 'joao@exemplo.com');

-- UPDATE: se a coluna indexada for alterada, o índice precisa ser atualizado
UPDATE clientes SET email = 'novo@exemplo.com' WHERE id = 1001;

-- DELETE: remove a entrada de todos os índices
DELETE FROM clientes WHERE id = 1001;

Índices demais podem piorar a performance de escrita. Uma tabela com 10 índices terá cada INSERT executando 10 operações adicionais de atualização de índice.

Além disso, índices sofrem fragmentação ao longo do tempo, especialmente com muitas operações de INSERT e DELETE. É necessário monitorar e eventualmente reconstruí-los:

-- PostgreSQL: reconstruir índice
REINDEX INDEX idx_clientes_email;

-- SQL Server: reorganizar índice
ALTER INDEX idx_clientes_email ON clientes REORGANIZE;

6. Índices compostos e dicas de criação

Índices podem abranger múltiplas colunas, sendo especialmente úteis para consultas com várias condições:

CREATE INDEX idx_pedidos_data_status ON pedidos(data_pedido, status);

A ordem das colunas importa. Coloque primeiro as colunas mais seletivas (com maior cardinalidade):

-- Bom: status tem apenas 3 valores, data_pedido tem muitos valores distintos
CREATE INDEX idx_pedidos_status_data ON pedidos(status, data_pedido);

-- Melhor: data_pedido primeiro, pois filtra mais registros rapidamente
CREATE INDEX idx_pedidos_data_status ON pedidos(data_pedido, status);

Índices covering (cobridores) contêm todas as colunas necessárias para uma consulta, evitando acesso à tabela principal:

-- Consulta que só precisa de email e nome
CREATE INDEX idx_covering ON clientes(email) INCLUDE (nome);
-- O índice já contém nome, então não precisa ler a tabela

7. Quando NÃO usar índices

Índices não são sempre benéficos. Evite criá-los nos seguintes casos:

  • Tabelas muito pequenas (menos de 1000 linhas): o custo de manter o índice supera o benefício
  • Colunas com baixa cardinalidade (ex: sexo, booleano): um índice em sexo pode até piorar a performance
  • Colunas raramente usadas em WHERE ou JOIN: índices não utilizados são apenas custo
  • Tabelas com alta frequência de escrita e baixa frequência de leitura: o overhead de manter índices é maior que o ganho
-- Exemplo de coluna que NÃO deve ser indexada
CREATE INDEX idx_sexo ON clientes(sexo);  -- Apenas 2 valores: 'M' e 'F'

8. Ferramentas para análise de índices

Para entender se seus índices estão sendo usados, utilize ferramentas de diagnóstico:

-- PostgreSQL: ver plano de execução
EXPLAIN ANALYZE SELECT * FROM clientes WHERE email = 'teste@exemplo.com';

-- Identificar scans sequenciais (falta de índice)
SELECT schemaname, tablename, seq_scan, seq_tup_read
FROM pg_stat_user_tables
ORDER BY seq_scan DESC;

-- MySQL: verificar uso de índices
EXPLAIN SELECT * FROM clientes WHERE email = 'teste@exemplo.com';

-- SQL Server: índices não utilizados
SELECT OBJECT_NAME(i.object_id) AS Tabela, i.name AS Indice
FROM sys.indexes i
WHERE i.index_id NOT IN (
    SELECT s.index_id FROM sys.dm_db_index_usage_stats s
    WHERE s.object_id = i.object_id
    AND s.database_id = DB_ID()
);

Para manter a performance, realize manutenções periódicas:

-- PostgreSQL: reconstruir índices fragmentados
REINDEX TABLE clientes;

-- SQL Server: rebuild com fragmentação > 30%
ALTER INDEX ALL ON clientes REBUILD;

Índices são ferramentas poderosas, mas exigem planejamento. Um índice bem escolhido pode transformar uma consulta de minutos em milissegundos. Por outro lado, índices desnecessários consomem espaço e degradam operações de escrita. A chave está em monitorar, testar e ajustar continuamente.


Referências