System Design/02 - Componentes/Caching2 min
Application Caching
- Que problema isso resolve?
- Quando usar / quando NÃO usar?
- Qual o principal trade-off?
- Como isso falha em produção?
Conceito
Cache dentro ou ao lado da aplicação, guardando objetos de domínio, resultados de query e computações caras. Normalmente Redis ou Memcached.
Cache local vs distribuído
| Local (in-process) | Distribuído (Redis) | |
|---|---|---|
| Latência | Nanossegundos | ~1 ms (rede) |
| Compartilhado | Não — um por instância | Sim |
| Invalidação | Difícil (N cópias) | Centralizada |
| Sobrevive a restart | Não | Sim |
| Limite | RAM do processo | Cluster |
O cache em duas camadas (local na frente do distribuído) combina os dois: latência de nanossegundos no caso comum, e o distribuído como fonte compartilhada. O preço é a inconsistência entre as cópias locais durante a janela de TTL.
Redis vs Memcached
| Redis | Memcached | |
|---|---|---|
| Estruturas | Lista, set, sorted set, hash, pub/sub | Só chave-valor |
| Persistência | Sim | Não |
| Replicação / failover | Sim | Não |
| Multi-thread | Não | Sim |
Trade-offs
Os dois problemas difíceis aparecem inteiros aqui.
Invalidação. TTL é a resposta preguiçosa e quase sempre certa: aceita-se dado desatualizado por N segundos em troca de nunca precisar rastrear dependências. Invalidação explícita é precisa e frágil — é fácil esquecer um caminho de escrita que deveria invalidar.
Stampede. Quando uma chave popular expira, todas as requisições concorrentes vão ao banco simultaneamente. As defesas: lock (uma requisição recalcula, as outras esperam), jitter no TTL (para as chaves não expirarem juntas) e Refresh Ahead.
Um terceiro problema: hot key. Uma chave muito acessada concentra tráfego num único nó do cluster, e nenhum sharding resolve — a saída é replicar a chave ou cachear localmente.
E a métrica: hit rate abaixo de ~80% raramente compensa. O cache adiciona um salto de rede, complexidade de invalidação e mais um componente para operar. Se ele erra na maioria das vezes, está cobrando tudo isso e entregando pouco.
Exemplo prático
O perfil de usuário é lido em toda requisição e alterado raramente:
perfil:{id} → TTL 300s + jitter de ±30s
O jitter é o detalhe que importa: sem ele, mil perfis carregados no mesmo deploy expiram no mesmo segundo e produzem uma rajada de misses simultâneos.
Na escrita do perfil, a aplicação invalida a chave explicitamente — assim a mudança aparece na hora, e o TTL fica como rede de segurança para os caminhos de escrita que alguém esquecer de cobrir.
Relacionado
Parte de Caching · roadmap.sh/system-design