O que e Cache
Cache e um mecanismo que armazena temporariamente copias de dados em um local de acesso rapido. Reduz o acesso a fonte de dados original (banco de dados, API, etc.) e diminui o tempo de resposta.
Efeito do Cache: Se uma consulta ao banco de dados leva 100ms, a recuperacao do cache pode ser concluida em menos de 1ms.
Camadas de Cache
Cache do Navegador
↓
Cache CDN
↓
Cache da Aplicacao (Redis, etc.)
↓
Cache do Banco de Dados
↓
Banco de Dados
Padroes de Cache
Cache-Aside
A aplicacao gerencia diretamente o cache e o banco de dados.
async function getUser(userId) {
// 1. Verificar cache
const cached = await cache.get(`user:${userId}`);
if (cached) {
return JSON.parse(cached);
}
// 2. Cache miss: buscar no DB
const user = await db.users.findById(userId);
// 3. Salvar no cache
await cache.setex(`user:${userId}`, 3600, JSON.stringify(user));
return user;
}
Vantagens: Simples, resistente a falhas Desvantagens: Latencia no cache miss
Read-Through
O proprio cache e responsavel pela obtencao de dados.
// Configuracao da biblioteca de cache
const cache = new Cache({
loader: async (key) => {
// Chamado automaticamente em cache miss
const userId = key.replace('user:', '');
return await db.users.findById(userId);
}
});
// Uso (simples!)
const user = await cache.get(`user:${userId}`);
Write-Through
Atualiza o cache e o DB simultaneamente durante a escrita.
async function updateUser(userId, data) {
// Atualizar DB
const user = await db.users.update(userId, data);
// Atualizar cache simultaneamente
await cache.setex(`user:${userId}`, 3600, JSON.stringify(user));
return user;
}
Vantagens: Alta consistencia de dados Desvantagens: Aumento da latencia de escrita
Write-Behind
Escreve imediatamente no cache e reflete no DB de forma assincrona.
async function updateUser(userId, data) {
// Atualizar cache imediatamente
await cache.setex(`user:${userId}`, 3600, JSON.stringify(data));
// Adicionar escrita no DB a fila
await writeQueue.add({ userId, data });
return data;
}
// Worker em background
writeQueue.process(async (job) => {
await db.users.update(job.userId, job.data);
});
Vantagens: Escrita rapida Desvantagens: Risco de perda de dados
Invalidacao de Cache
TTL (Time To Live)
Expira automaticamente apos um determinado tempo.
// Expira apos 60 segundos
await cache.setex('key', 60, 'value');
Invalidacao Baseada em Eventos
Remove explicitamente o cache ao atualizar dados.
async function updateUser(userId, data) {
await db.users.update(userId, data);
// Invalidar caches relacionados
await cache.del(`user:${userId}`);
await cache.del(`user:${userId}:profile`);
await cache.del(`users:list`);
}
Invalidacao Baseada em Padrao
// Excluir todos os caches relacionados ao usuario
const keys = await cache.keys('user:123:*');
await cache.del(...keys);
Problemas de Cache e Solucoes
Cache Stampede (Avalanche)
Problema onde muitas requisicoes causam cache miss simultaneamente.
// Solucao: usar lock
async function getWithLock(key, loader) {
const cached = await cache.get(key);
if (cached) return JSON.parse(cached);
// Obter lock
const lockKey = `lock:${key}`;
const locked = await cache.set(lockKey, '1', 'NX', 'EX', 10);
if (!locked) {
// Outro processo esta carregando → esperar e tentar novamente
await sleep(100);
return getWithLock(key, loader);
}
try {
const data = await loader();
await cache.setex(key, 3600, JSON.stringify(data));
return data;
} finally {
await cache.del(lockKey);
}
}
Recalculo Probabilistico Antecipado
Atualiza o cache probabilisticamente antes do TTL expirar.
async function getWithProbabilisticRefresh(key, loader, ttl) {
const data = await cache.get(key);
const remainingTtl = await cache.ttl(key);
// Se TTL restante e baixo, recalcular probabilisticamente
if (data && remainingTtl < ttl * 0.1) {
if (Math.random() < 0.1) {
// 10% de chance de atualizacao em background
loader().then(newData => {
cache.setex(key, ttl, JSON.stringify(newData));
});
}
}
if (data) return JSON.parse(data);
const newData = await loader();
await cache.setex(key, ttl, JSON.stringify(newData));
return newData;
}
Design de Chaves de Cache
// Bom design de chave
const key = `user:${userId}:profile:v2`;
// Componentes:
// - Prefixo: tipo de entidade
// - Identificador: ID unico
// - Sub-recurso: dados especificos
// - Versao: compatibilidade em mudancas de schema
Diretrizes de Design de TTL
| Tipo de Dados | TTL | Motivo |
|---|---|---|
| Conteudo estatico | 1 dia a 1 semana | Raramente muda |
| Perfil de usuario | 1 a 24 horas | Baixa frequencia de mudanca |
| Informacoes de configuracao | 5 a 30 minutos | Atualizado moderadamente |
| Dados em tempo real | 1 a 5 minutos | Muda frequentemente |
| Sessao | 30 minutos a 24 horas | Equilibrio entre seguranca e UX |
Resumo
Cache e uma tecnica fundamental de otimizacao de performance. Ao entender padroes como Cache-Aside e Write-Through e projetar estrategias apropriadas de TTL e invalidacao, voce pode construir sistemas rapidos e escalaveis. Considere o equilibrio entre complexidade e beneficios do cache ao implementa-lo.
← Voltar para a lista