Cache em JavaScript: Redis, Memcached e soluções integradas: do A ao Z
No desenvolvimento web moderno com Node.js, o desempenho da aplicação depende diretamente da velocidade de acesso aos dados. Cada requisição ao banco de dados, API externa ou sistema de arquivos cria uma latência que se acumula com o aumento da carga. O cache é uma estratégia de armazenamento temporário de dados frequentemente solicitados em um armazenamento rápido, permitindo reduzir o tempo de resposta em 10 a 100 vezes. Neste artigo, abordaremos três abordagens de cache em JavaScript: soluções integradas (Map, WeakMap, módulo node-cache), sistemas distribuídos Redis e Memcached, além de suas combinações.
Por que o cache é necessário no Node.js?
O principal objetivo do cache é reduzir a carga em recursos lentos (bancos de dados, discos, serviços externos) e acelerar a entrega de dados ao usuário. Em uma aplicação web típica em Node.js, o cache pode armazenar:
- Resultados de consultas complexas ao BD (agregações, JOIN).
- Respostas de APIs externas (clima, taxas de câmbio, dados geográficos).
- Sessões de usuários e tokens de autenticação.
- Fragmentos HTML ou páginas inteiras (cache SSR).
- Arquivos de configuração e templates.
Sem cache, cada acesso repetido aos mesmos dados resulta em trabalho duplicado. Com cache, os dados são recuperados da memória RAM em microssegundos.
Soluções integradas para cache em JavaScript
Para cenários simples ou aplicações single-thread, podem ser usadas estruturas de dados nativas do JavaScript. Elas não exigem instalação de pacotes externos, mas possuem limitações de volume e tempo de vida.
1. Map e WeakMap
O objeto Map é uma coleção chave-valor, ideal para cache dentro de um único processo. Exemplo de um cache simples com TTL (time-to-live):
class SimpleCache { constructor(ttl = 60000) { this.cache = new Map(); this.ttl = ttl; }
set(key, value) { this.cache.set(key, { value, expiry: Date.now() + this.ttl }); }
get(key) { const item = this.cache.get(key); if (!item) return null; if (Date.now() > item.expiry) { this.cache.delete(key); return null; } return item.value; }
has(key) { return this.get(key) !== null; }}
// Usoconst cache = new SimpleCache(5000); // TTL 5 segundoscache.set('user:123', { name: 'João' });console.log(cache.get('user:123')); // { name: 'João' }O WeakMap difere porque suas chaves são apenas objetos, e o garbage collector pode remover entradas se não houver mais referências ao objeto-chave. Isso é útil para armazenar em cache elementos DOM ou objetos temporários.
2. Módulo node-cache
Para ambientes de produção, é melhor usar a biblioteca pronta node-cache. Ela suporta TTL, limpeza automática e funciona mais rápido que uma solução caseira:
const NodeCache = require('node-cache');const myCache = new NodeCache({ stdTTL: 100, checkperiod: 120 });
// EscritamyCache.set('key', { data: 'value' });
// Leituraconst value = myCache.get('key');if (value === undefined) { // Cache miss — precisa obter dados do BD}
// RemoçãomyCache.del('key');Soluções integradas são adequadas para aplicações monolíticas ou microsserviços com uma única instância. No entanto, ao escalar para vários processos ou servidores, os dados tornam-se inconsistentes — aqui são necessários caches distribuídos.
Redis: poderoso armazenamento de dados in-memory
Redis é um armazenamento de dados de alto desempenho em memória RAM, que suporta strings, hashes, listas, conjuntos e outras estruturas. Para Node.js, são usados os pacotes redis ou ioredis. Redis é ideal para cache devido a:
- Suporte a TTL no nível da chave (comando
EXPIRE). - Operações atômicas (INC