System Design/02 - Componentes/Caching2 min
Cache-Aside
- Que problema isso resolve?
- Quando usar / quando NÃO usar?
- Qual o principal trade-off?
- Como isso falha em produção?
Conceito
Também chamado lazy loading. A aplicação gerencia o cache explicitamente: consulta primeiro, e só busca na fonte quando erra.
ler(chave):
valor = cache.get(chave)
se valor existe: return valor # HIT
valor = banco.get(chave) # MISS
cache.set(chave, valor, ttl)
return valor
Na escrita, o padrão usual é escrever no banco e invalidar o cache — não atualizar o cache:
escrever(chave, valor):
banco.set(chave, valor)
cache.delete(chave) # invalida, não atualiza
Invalidar em vez de atualizar evita uma corrida clássica: duas escritas concorrentes podem gravar no cache em ordem inversa à do banco, deixando o cache permanentemente errado.
Trade-offs
| Ganha | Perde |
|---|---|
| Só cacheia o que é realmente pedido | Todo primeiro acesso é lento |
| Resiliente: cache fora do ar ≠ sistema fora | Dado pode ficar obsoleto (janela de TTL) |
| Simples de entender e implementar | Três idas na primeira leitura |
É o padrão mais usado justamente pelo equilíbrio: memória proporcional ao uso real, e falha do cache degrada performance sem derrubar o sistema.
Os problemas conhecidos:
- Cache stampede — chave popular expira e N requisições vão ao banco juntas. Defesas: lock, jitter no TTL, Refresh Ahead.
- Cache penetration — pedidos por chaves que não existem nunca populam o cache e sempre batem no banco. Defesa: cachear o "não existe" com TTL curto, ou um Bloom filter.
- Janela de inconsistência — entre a escrita no banco e a invalidação, uma leitura concorrente pode repopular o cache com o valor velho.
Exemplo prático
Catálogo de produtos com 100 mil itens, dos quais ~2 mil concentram o tráfego.
Com cache-aside, a memória usada tende a esses 2 mil — o cache se molda ao padrão real de acesso, sem ninguém precisar prever quais são os populares. Com Write-through, os 100 mil ocupariam espaço, incluindo os que ninguém consulta.
O custo aparece no primeiro acesso a cada produto (~50 ms em vez de ~1 ms) e num detalhe operacional: depois de limpar o cache ou reiniciar o cluster, todo o tráfego cai no banco de uma vez. Reaquecer o cache antes de liberar tráfego é parte do procedimento.
Relacionado
Parte de Caching · roadmap.sh/system-design