Guides
Fundamentos ▾
Versionamento ▾
Deploy ▾

Stack

UsoLib Node.js
Cache / dados genéricosioredis
Sessões Expressexpress-session + connect-redis
Filas / workersbullmq

1 Instalação

Docker Compose — desenvolvimento

services:
  redis:
    image: redis:7-alpine
    container_name: redis
    ports:
      - "127.0.0.1:6379:6379"
    volumes:
      - ./redis_data:/data
    command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
    restart: unless-stopped

Produção (servidor)

docker network create redis-network
mkdir -p /home/redis
# /home/redis/docker-compose.yml
name: redis
services:
  redis:
    image: redis:7-alpine
    container_name: redis
    ports:
      - "127.0.0.1:6379:6379"    # bind local — nunca 0.0.0.0
    volumes:
      - ./redis_data:/data
    command: >
      redis-server
      --appendonly yes
      --maxmemory 512mb
      --maxmemory-policy allkeys-lru
      --requirepass ${REDIS_PASSWORD}
    restart: unless-stopped
    networks:
      - redis-network
networks:
  redis-network:
    external: true
# /home/redis/.env
REDIS_PASSWORD=senha-redis-forte

chmod 600 /home/redis/.env
cd /home/redis && docker compose up -d

2 redis-cli

redis-cli                         # conectar local
redis-cli -h 127.0.0.1 -p 6379
redis-cli -a "senha"

PING                              # → PONG
INFO server                       # versão, uptime
INFO memory                       # uso de memória
DBSIZE                            # quantidade de chaves
FLUSHDB                           # apaga DB atual (cuidado)
MONITOR                           # stream em tempo real de comandos

3 Strings

SET usuario:1:nome "Alfredo"
GET usuario:1:nome                # → "Alfredo"

SET contador 0
INCR contador                     # → 1
INCRBY contador 5                 # → 6

SET cache:produto:42 '{"id":42}' EX 3600   # expira em 1h
MSET chave1 "a" chave2 "b"
MGET chave1 chave2                # → ["a","b"]

EXISTS chave1                     # → 1 / 0
DEL chave1 chave2
TYPE chave1                       # → string / hash / list / set / zset

4 TTL — Expiração

SET sessao:abc123 "dados" EX 1800       # expira em 30 min
SET token:xyz "jwt"   PX 60000          # expira em 60s (milissegundos)

TTL sessao:abc123         # → segundos restantes (-1 permanente, -2 não existe)
PTTL sessao:abc123        # → milissegundos

EXPIRE sessao:abc123 3600 # renova TTL
PERSIST sessao:abc123     # remove expiração

5 Hashes

HSET usuario:1 nome "Alfredo" email "a@a.com" role "admin"
HGET usuario:1 nome               # → "Alfredo"
HGETALL usuario:1                 # → {nome, email, role}
HMGET usuario:1 nome email        # → ["Alfredo","a@a.com"]
HDEL usuario:1 ultimo_login
HEXISTS usuario:1 email           # → 1
HINCRBY usuario:1 pontos 10       # incrementa campo numérico

6 Lists

RPUSH fila:emails "e1" "e2" "e3"  # adiciona no final
LPUSH fila:emails "e0"            # adiciona no início
LRANGE fila:emails 0 -1           # lista completa
LLEN fila:emails
LPOP fila:emails                  # remove e retorna do início
RPOP fila:emails                  # remove e retorna do final
BLPOP fila:emails 30              # blocking pop — espera até 30s

7 Sets e Sorted Sets

# Set — sem ordem, sem duplicatas
SADD tags:artigo:1 "node" "typescript" "redis"
SMEMBERS tags:artigo:1
SISMEMBER tags:artigo:1 "redis"   # → 1
SUNION tags:artigo:1 tags:artigo:2
SINTER tags:artigo:1 tags:artigo:2

# Sorted Set — com score, ordenado
ZADD ranking 1500 "usuario:1" 2300 "usuario:2"
ZREVRANGE ranking 0 2 WITHSCORES  # top 3
ZINCRBY ranking 100 "usuario:1"
ZREVRANK ranking "usuario:2"       # posição (0-based, decrescente)

8 Node.js com ioredis

npm install ioredis
// lib/redis.ts
import Redis from 'ioredis';

const redis = new Redis({
  host: process.env.REDIS_HOST || '127.0.0.1',
  port: Number(process.env.REDIS_PORT) || 6379,
  password: process.env.REDIS_PASSWORD,
  maxRetriesPerRequest: 3,
  lazyConnect: true,
});

redis.on('error', (err) => console.error('Redis error:', err));
export default redis;

9 Cache de Queries

async function withCache<T>(
  key: string,
  ttlSeconds: number,
  fn: () => Promise<T>
): Promise<T> {
  const cached = await redis.get(key);
  if (cached) return JSON.parse(cached) as T;

  const data = await fn();
  await redis.setex(key, ttlSeconds, JSON.stringify(data));
  return data;
}

// uso
const produtos = await withCache('produtos:todos', 300, () =>
  db.query('SELECT * FROM produtos')
);

// invalidar
await redis.del('produtos:todos');

// invalidar por padrão (SCAN, nunca KEYS em produção)
async function deletarPorPadrao(padrao: string) {
  let cursor = '0';
  do {
    const [novoCursor, chaves] = await redis.scan(cursor, 'MATCH', padrao, 'COUNT', 100);
    cursor = novoCursor;
    if (chaves.length) await redis.del(...chaves);
  } while (cursor !== '0');
}

await deletarPorPadrao('produtos:*');

10 Sessões Express

npm install express-session connect-redis
import session from 'express-session';
import RedisStore from 'connect-redis';
import redis from './lib/redis';

app.use(session({
  store: new RedisStore({ client: redis }),
  secret: process.env.SESSION_SECRET!,
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: process.env.NODE_ENV === 'production',
    httpOnly: true,
    maxAge: 24 * 60 * 60 * 1000,   // 24h
    sameSite: 'lax',
  },
}));

// login
req.session.userId = usuario.id;

// proteger rota
if (!req.session.userId) return res.status(401).json({ erro: 'Não autenticado' });

// logout
req.session.destroy(() => res.json({ ok: true }));

11 Rate Limiting

async function rateLimiter(ip: string, limite: number, janelaSegundos: number) {
  const chave = `rl:${ip}`;
  const atual = await redis.incr(chave);
  if (atual === 1) await redis.expire(chave, janelaSegundos);
  return { permitido: atual <= limite, restante: Math.max(0, limite - atual) };
}

// middleware
app.use(async (req, res, next) => {
  const { permitido, restante } = await rateLimiter(req.ip!, 100, 60);
  res.setHeader('X-RateLimit-Remaining', restante);
  if (!permitido) return res.status(429).json({ erro: 'Muitas requisições' });
  next();
});

12 Pub/Sub

// publisher.ts
const pub = new Redis();
await pub.publish('canal:notificacoes', JSON.stringify({ tipo: 'novo-pedido', id: 42 }));

// subscriber.ts — instância separada
const sub = new Redis();
sub.subscribe('canal:notificacoes');
sub.on('message', (canal, mensagem) => {
  console.log(`[${canal}]`, JSON.parse(mensagem));
});
Nota: subscriber não pode executar outros comandos Redis — use uma instância separada.

13 BullMQ — Filas

npm install bullmq
// queue.ts — adicionar jobs
import { Queue } from 'bullmq';
const emailQueue = new Queue('emails', { connection: redis });

await emailQueue.add('boas-vindas', { para: 'user@email.com', nome: 'Alfredo' }, {
  attempts: 3,
  backoff: { type: 'exponential', delay: 2000 },
  removeOnComplete: 100,
  removeOnFail: 500,
});

// worker.ts — processar jobs
import { Worker } from 'bullmq';
const worker = new Worker('emails', async (job) => {
  await enviarEmail(job.data.para, job.data.nome);
}, { connection: redis, concurrency: 5 });

worker.on('completed', (job) => console.log(`Job ${job.id} ok`));
worker.on('failed', (job, err) => console.error(`Job ${job?.id} falhou:`, err));

14 Boas Práticas

RegraMotivo
Usar SCAN, nunca KEYSKEYS bloqueia o servidor em produção
Sempre definir TTL em cacheEvita crescimento ilimitado da memória
Convenção tipo:id:campoNamespacing para evitar colisões
Bind 127.0.0.1 ou rede DockerNunca expor porta para 0.0.0.0
Usar pipeline para múltiplos comandosReduz round-trips de rede
Definir maxmemory-policyControla o que descartar quando cheio
// pipeline — vários comandos em uma viagem
const pipeline = redis.pipeline();
pipeline.set('a', '1');
pipeline.set('b', '2');
pipeline.incr('contador');
const resultados = await pipeline.exec();

15 Monitoramento

redis-cli INFO all                    # estatísticas completas
redis-cli INFO memory | grep used_memory_human
redis-cli INFO clients                # conexões ativas
redis-cli INFO stats | grep instantaneous_ops
redis-cli --latency -h 127.0.0.1     # latência em tempo real
redis-cli MONITOR                     # stream de todos os comandos
redis-cli --bigkeys                   # top chaves por tamanho
redis-cli MEMORY USAGE nome:da:chave  # memória de uma chave