🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

Como usar Redis para armazenamento em cache em um aplicativo Node.js: guia completo de 2026

⏱️5 min read  ·  1,059 words

Redis é um armazenamento de dados na memória que acelera drasticamente os aplicativos armazenando em cache consultas caras de banco de dados e respostas de API. Um cache bem colocado pode transformar uma consulta de banco de dados de 200 ms em um acerto de cache de 2 ms. Este guia adiciona cache Redis a um aplicativo Node.js da maneira certa.

Por que armazenar em cache com Redis?

  • Velocidade: As leituras na memória são 10-100x mais rápidas que as consultas ao banco de dados
  • Reduza a carga do banco de dados: Atenda solicitações repetidas do cache, poupando seu banco de dados
  • Escala: Lide com picos de tráfego sem sobrecarregar seu armazenamento de dados primário
  • Versátil: Também lida com sessões, limitação de taxa, filas e pub/sub

Configuração

npm install ioredis

# Run Redis locally with Docker
docker run -d --name redis -p 6379:6379 redis:7-alpine
// redis.js — connection singleton
import Redis from 'ioredis';

export const redis = new Redis({
  host: process.env.REDIS_HOST || 'localhost',
  port: parseInt(process.env.REDIS_PORT || '6379'),
  maxRetriesPerRequest: 3,
  retryStrategy: (times) => Math.min(times * 50, 2000),
});

redis.on('error', (err) => console.error('Redis error:', err));
redis.on('connect', () => console.log('Redis connected'));

O padrão Cache-Aside (mais comum)

Verifique o cache primeiro; em caso de erro, busque no banco de dados e preencha o cache:

import { redis } from './redis';

async function getUser(userId) {
  const cacheKey = `user:${userId}`;

  // 1. Try cache first
  const cached = await redis.get(cacheKey);
  if (cached) {
    return JSON.parse(cached);  // cache hit — fast path
  }

  // 2. Cache miss — fetch from database
  const user = await db.users.findById(userId);
  if (!user) return null;

  // 3. Populate cache with a TTL (expire after 1 hour)
  await redis.set(cacheKey, JSON.stringify(user), 'EX', 3600);

  return user;
}

Wrapper de cache reutilizável

async function cached(key, ttlSeconds, fetchFn) {
  const hit = await redis.get(key);
  if (hit) return JSON.parse(hit);

  const data = await fetchFn();
  if (data !== null && data !== undefined) {
    await redis.set(key, JSON.stringify(data), 'EX', ttlSeconds);
  }
  return data;
}

// Usage — clean and reusable
const products = await cached('products:featured', 600, () =>
  db.products.findFeatured()
);

const user = await cached(`user:${id}`, 3600, () =>
  db.users.findById(id)
);

Invalidação de cache (a parte difícil)

// When data changes, invalidate the cache to avoid stale reads

async function updateUser(userId, updates) {
  const user = await db.users.update(userId, updates);

  // Invalidate the cached version
  await redis.del(`user:${userId}`);

  // Invalidate related caches too
  await redis.del('users:list');

  return user;
}

// Invalidate by pattern (use SCAN, not KEYS, in production)
async function invalidatePattern(pattern) {
  const stream = redis.scanStream({ match: pattern, count: 100 });
  stream.on('data', (keys) => {
    if (keys.length) redis.del(...keys);
  });
}
// invalidatePattern('user:*');  // clear all user caches

Middleware expresso para cache de rota

function cacheRoute(ttlSeconds) {
  return async (req, res, next) => {
    const key = `route:${req.originalUrl}`;
    const cached = await redis.get(key);

    if (cached) {
      res.setHeader('X-Cache', 'HIT');
      return res.json(JSON.parse(cached));
    }

    // Override res.json to cache the response
    const originalJson = res.json.bind(res);
    res.json = (body) => {
      redis.set(key, JSON.stringify(body), 'EX', ttlSeconds);
      res.setHeader('X-Cache', 'MISS');
      return originalJson(body);
    };
    next();
  };
}

// Cache this route for 5 minutes
app.get('/api/products', cacheRoute(300), getProducts);

Limitação de taxa com Redis

async function rateLimit(req, res, next) {
  const key = `ratelimit:${req.ip}`;
  const limit = 100;      // requests
  const window = 60;      // seconds

  const current = await redis.incr(key);
  if (current === 1) {
    await redis.expire(key, window);  // set TTL on first request
  }

  if (current > limit) {
    return res.status(429).json({ error: 'Too many requests' });
  }

  res.setHeader('X-RateLimit-Remaining', Math.max(0, limit - current));
  next();
}

Práticas recomendadas de armazenamento em cache

  • Sempre defina um TTL: Impede que dados obsoletos vivam para sempre e controla a memória
  • Armazene em cache o que é caro e lido com frequência: Não armazene tudo em cache — armazene em cache os caminhos quentes
  • Lide com falhas de cache normalmente: Se o Redis estiver inativo, acesse o banco de dados, não trave
  • Use nomenclatura de chave consistente: entity:id:field convenções tornam a invalidação previsível
  • Evite o comando KEYS em produção: Ele bloqueia Redis – use SCAN
  • Fique atento às debandadas de cache: Quando uma chave popular expira, muitas solicitações chegam ao banco de dados de uma só vez — use bloqueios ou TTLs escalonados

Perguntas Frequentes

P: O que NÃO devo armazenar em cache?
R: Dados altamente voláteis que mudam a cada solicitação, dados confidenciais sem segurança adequada e dados baratos para serem computados. Armazene em cache os dados caros, lidos com frequência e relativamente estáveis.

P: Como escolho um TTL?
R: Equilibre frescor versus desempenho. Perfis de usuário: 1 hora. Listagens de produtos: 5 a 10 minutos. Dados em tempo real: segundos ou não armazenados em cache. Combine o TTL com a frequência com que os dados realmente mudam.

P: O que acontece se o Redis falhar?
R: Seu aplicativo deve ser degradado normalmente – detecte erros do Redis e caia no banco de dados. Nunca deixe que uma falha de cache derrube seu aplicativo. Envolva chamadas de cache em try/catch.

P: Redis versus cache na memória (como um mapa JS)?
R: Os caches de mapas na memória são por processo (perdidos na reinicialização, não compartilhados entre instâncias). O Redis é compartilhado entre todas as instâncias do seu aplicativo e persiste – essencial para aplicativos dimensionados horizontalmente.

P: Como evito debandadas de cache?
R: Use um bloqueio (SET NX) para que apenas uma solicitação atualize o cache enquanto outras aguardam ou fornecem dados obsoletos, ou adicione instabilidade aleatória aos TTLs para que as chaves populares não expirem todas simultaneamente.

Conclusão

O cache Redis pode transformar o desempenho do seu aplicativo Node.js, transformando consultas lentas ao banco de dados em acessos instantâneos ao cache. O padrão cache-aside com um wrapper reutilizável cobre a maioria das necessidades, o middleware em nível de rota armazena em cache respostas inteiras e a mesma instância do Redis lida com limitação de taxa e sessões. Lembre-se das duas partes difíceis: sempre defina um TTL e invalide o cache quando os dados forem alterados. Lide com falhas do Redis com facilidade e seu aplicativo ficará mais rápido e resiliente ao mesmo tempo.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা