Sua query funcionava perfeitamente com mil registros e agora, com dez milhões, leva trinta segundos. Quase sempre a explicação está nos índices — ou na falta deles. Entender índices é o que transforma um profissional pleno em alguém que escreve consultas que escalam.
A analogia do índice do livro
Imagine procurar a palavra "transação" num livro de 800 páginas sem índice remissivo: você lê página por página, do começo ao fim. Com o índice no final do livro, você vai direto: "transação ... pág. 412". Um índice de banco de dados faz exatamente isso — é uma estrutura à parte que mapeia valores para a localização das linhas, evitando varrer a tabela inteira.
B-tree: a estrutura por trás
A maioria dos índices usa uma B-tree (árvore balanceada). Você não precisa implementá-la, mas a noção importa: os valores ficam ordenados em uma árvore, e o banco encontra qualquer um descendo poucos níveis — em vez de N linhas, ele faz cerca de log(N) comparações. Por isso um índice transforma uma busca de milhões de linhas em pouquíssimos passos.
O que um índice acelera
Índices ajudam sempre que o banco precisa localizar ou ordenar dados:
- WHERE — filtrar por uma coluna indexada.
- JOIN — casar linhas entre tabelas pela coluna de junção.
- ORDER BY — os dados no índice já estão ordenados.
Por isso chaves estrangeiras e colunas muito filtradas são candidatas naturais a índice.
O custo: escrita mais lenta
Índice não é mágica grátis. Cada índice é uma estrutura que precisa ser atualizada a cada INSERT, UPDATE ou DELETE. Mais índices significam escrita mais lenta e mais espaço em disco.
| Operação | Sem índice | Com índice |
|---|---|---|
| Leitura (WHERE/JOIN) | Lenta (varre tudo) | Rápida |
| Escrita (INSERT/UPDATE) | Rápida | Mais lenta (atualiza o índice) |
| Espaço em disco | Menor | Maior |
A decisão de indexar é sempre um equilíbrio entre leitura e escrita.
Índice composto e a ordem das colunas
Um índice composto cobre várias colunas juntas, e a ordem importa. Pense numa lista telefônica ordenada por (sobrenome, nome): ela serve para buscar por sobrenome, ou por sobrenome+nome, mas é inútil para buscar só pelo nome. A regra é o "prefixo mais à esquerda": o índice (a, b) ajuda em filtros por a ou por a e b, mas não por b sozinho.
Lendo o plano de execução (EXPLAIN)
Para saber o que o banco realmente faz, use EXPLAIN. Ele revela se a query usa o índice (index scan) ou varre a tabela toda (full table scan / seq scan) — o vilão das queries lentas.
EXPLAIN SELECT * FROM pedidos WHERE cliente_id = 42;
-- Sem índice:
-- Seq Scan on pedidos (cost=0.00..18234 rows=1)
-- Após CREATE INDEX idx_cli ON pedidos(cliente_id):
-- Index Scan using idx_cli (cost=0.42..8.44 rows=1)
A queda no custo estimado mostra o ganho. Quando você vê "Seq Scan" numa tabela grande filtrada por uma coluna, é o sinal clássico de índice faltando.
Índice não é "quanto mais, melhor". É "qual coluna eu filtro tanto que vale pagar o custo de escrita?". Quem responde isso lendo o EXPLAIN, e não no chute, otimiza de verdade.
É comum em entrevista entregarem uma query lenta e pedirem para você diagnosticar — a resposta esperada começa em "rodaria o EXPLAIN para ver se há full scan". No dia a dia isso aparece toda vez que um relatório que era instantâneo começa a travar conforme a tabela cresce.
Conclusão
Índices aceleram leituras ao custo de escritas mais lentas; B-trees explicam por que são tão eficientes; a ordem das colunas define o que um índice composto cobre; e o EXPLAIN é seu raio-X para ver full scan versus index scan. Com isso, "minha query está lenta" deixa de ser um mistério e vira um diagnóstico objetivo.
Your query worked perfectly with a thousand records, and now, with ten million, it takes thirty seconds. Almost always the explanation lies in indexes — or the lack of them. Understanding indexes is what turns a mid-level professional into someone who writes queries that scale.
The book index analogy
Imagine looking for the word "transaction" in an 800-page book with no index: you read page by page, from start to finish. With the index at the back of the book, you go straight there: "transaction ... p. 412". A database index does exactly that — it's a separate structure that maps values to the location of rows, avoiding a scan of the whole table.
B-tree: the structure behind it
Most indexes use a B-tree (balanced tree). You don't need to implement one, but the idea matters: values are kept sorted in a tree, and the database finds any of them by descending just a few levels — instead of N rows, it makes about log(N) comparisons. That's why an index turns a search over millions of rows into just a handful of steps.
What an index speeds up
Indexes help whenever the database needs to locate or sort data:
- WHERE — filtering by an indexed column.
- JOIN — matching rows between tables by the join column.
- ORDER BY — the data in the index is already sorted.
That's why foreign keys and heavily filtered columns are natural candidates for an index.
The cost: slower writes
An index isn't free magic. Each index is a structure that has to be updated on every INSERT, UPDATE or DELETE. More indexes mean slower writes and more disk space.
| Operation | Without index | With index |
|---|---|---|
| Read (WHERE/JOIN) | Slow (scans everything) | Fast |
| Write (INSERT/UPDATE) | Fast | Slower (updates the index) |
| Disk space | Smaller | Larger |
The decision to index is always a balance between reads and writes.
Composite index and column order
A composite index covers several columns together, and order matters. Think of a phone book sorted by (last name, first name): it works for searching by last name, or by last name + first name, but it's useless for searching by first name alone. The rule is the "leftmost prefix": the index (a, b) helps with filters on a or on a and b, but not on b alone.
Reading the execution plan (EXPLAIN)
To find out what the database is really doing, use EXPLAIN. It reveals whether the query uses the index (index scan) or scans the whole table (full table scan / seq scan) — the villain of slow queries.
EXPLAIN SELECT * FROM pedidos WHERE cliente_id = 42;
-- Without an index:
-- Seq Scan on pedidos (cost=0.00..18234 rows=1)
-- After CREATE INDEX idx_cli ON pedidos(cliente_id):
-- Index Scan using idx_cli (cost=0.42..8.44 rows=1)
The drop in estimated cost shows the gain. When you see "Seq Scan" on a large table filtered by a column, it's the classic sign of a missing index.
An index isn't "the more, the better". It's "which column do I filter on so often that it's worth paying the write cost?". Whoever answers that by reading EXPLAIN, rather than guessing, truly optimizes.
In interviews they often hand you a slow query and ask you to diagnose it — the expected answer starts with "I'd run EXPLAIN to see if there's a full scan". Day to day, this shows up every time a report that used to be instant starts to hang as the table grows.
Conclusion
Indexes speed up reads at the cost of slower writes; B-trees explain why they're so efficient; column order defines what a composite index covers; and EXPLAIN is your X-ray for telling a full scan from an index scan. With that, "my query is slow" stops being a mystery and becomes an objective diagnosis.