Developer Tools

Redis Além do Cache: Estruturas de Dados, Pub/Sub e Aplicações em Tempo Real

Aprenda a usar o Redis além de um simples cache — explore suas estruturas de dados, mensageria pub/sub, streams e padrões para construir aplicações rápidas e escaláveis.

10 min de leitura

Sala de servidores com conexões luminosas

A maioria dos desenvolvedores conhece o Redis como "aquele cache rápido". Mas o Redis é um servidor de estruturas de dados em memória completo, capaz de lidar com gerenciamento de sessões, rankings em tempo real, limitação de taxa, filas de mensagens e muito mais — tudo com latência abaixo de milissegundo.

Por que o Redis é tão rápido

O Redis armazena tudo na memória e utiliza um loop de eventos single-threaded para processar comandos. Sem I/O em disco nas leituras, sem contenção de locks, sem troca de contexto. O resultado: mais de 100.000 operações por segundo em hardware modesto.

Comparação típica de latência:
┌─────────────────┬──────────────┐
│ Operação        │ Latência     │
├─────────────────┼──────────────┤
│ Redis GET       │ 0,1 ms       │
│ PostgreSQL SEL  │ 1-5 ms       │
│ Chamada REST API│ 50-200 ms    │
│ Leitura em disco│ 5-10 ms      │
└─────────────────┴──────────────┘

Estruturas de dados principais

O Redis não é apenas chave-valor. Ele suporta estruturas de dados ricas que se encaixam diretamente nas necessidades mais comuns de aplicações.

Strings — o bloco fundamental

Strings armazenam texto, números ou dados binários de até 512 MB:

SET user:1001:name "Alice"
GET user:1001:name          # "Alice"

# Contador atômico
INCR page:views             # 1, 2, 3, ...
INCRBY cart:total 2500      # Adiciona 25,00 (armazena em centavos)

# Chaves com expiração (TTL)
SET session:abc123 "{...}" EX 3600   # Expira em 1 hora
TTL session:abc123                    # Segundos restantes

Hashes — objetos leves

Hashes são ideais para armazenar objetos sem overhead de serialização:

HSET user:1001 name "Alice" email "alice@example.com" plan "pro"
HGET user:1001 name         # "Alice"
HGETALL user:1001           # Todos os campos e valores
HINCRBY user:1001 logins 1  # Incremento atômico de campo

Hashes usam 10x menos memória do que armazenar cada campo como uma chave separada.

Lists — filas e feeds

Coleções ordenadas que suportam push/pop em ambas as extremidades:

LPUSH notifications:alice "Novo comentário na sua postagem"
LPUSH notifications:alice "Você tem um novo seguidor"
LRANGE notifications:alice 0 9    # Últimas 10 notificações

# Usar como fila (produtor/consumidor)
RPUSH queue:emails "send-welcome"
LPOP queue:emails                  # Processa o próximo job

Sets — coleções únicas

Coleções não ordenadas de strings únicas:

SADD tags:post:42 "redis" "database" "nosql"
SMEMBERS tags:post:42             # Todas as tags
SISMEMBER tags:post:42 "redis"    # true

# Operações com sets
SINTER tags:post:42 tags:post:99  # Tags em comum
SUNION tags:post:42 tags:post:99  # Todas as tags combinadas

Sorted Sets — rankings e leaderboards

Sets com uma pontuação para cada membro, ordenados automaticamente:

ZADD leaderboard 1500 "alice" 2300 "bob" 1800 "charlie"
ZREVRANGE leaderboard 0 2 WITHSCORES   # Top 3 jogadores
ZRANK leaderboard "alice"               # Posição (base 0)
ZINCRBY leaderboard 100 "alice"         # Alice marca 100 pontos

É assim que leaderboards de jogos, posts em alta e filas de prioridade são construídos em escala.

Padrões do mundo real

Padrão 1: Armazenamento de sessões

Em vez de sessões baseadas em banco de dados, use o Redis para buscas instantâneas:

import redis
r = redis.Redis()

# Armazenar sessão
r.setex(f"session:{token}", 3600, json.dumps(user_data))

# Recuperar sessão
data = r.get(f"session:{token}")

Por quê? Sessões em banco de dados adicionam 1-5ms por requisição. Sessões no Redis adicionam 0,1ms. A 1000 req/s, isso representa 5 segundos economizados a cada segundo.

Padrão 2: Limitação de taxa

Rate limiter com janela deslizante em 3 comandos:

# Permitir 100 requisições por minuto por usuário
MULTI
INCR rate:user:1001
EXPIRE rate:user:1001 60
EXEC

# Verificar: se o resultado do INCR > 100, rejeitar a requisição

Padrão 3: Cache com cache-aside

O padrão de cache mais comum:

def get_user(user_id):
    # 1. Verificar o cache
    cached = redis.get(f"user:{user_id}")
    if cached:
        return json.loads(cached)
    
    # 2. Cache miss → consultar o banco de dados
    user = db.query("SELECT * FROM users WHERE id = %s", user_id)
    
    # 3. Popular o cache (expira em 5 minutos)
    redis.setex(f"user:{user_id}", 300, json.dumps(user))
    return user

Padrão 4: Pub/Sub para eventos em tempo real

O Redis Pub/Sub permite mensageria em tempo real entre serviços:

# Subscriber (aguarda mensagens)
SUBSCRIBE chat:room:42

# Publisher (envia mensagens)
PUBLISH chat:room:42 "Olá a todos!"

No código da aplicação:

# Publisher
redis.publish("notifications", json.dumps({
    "type": "new_order",
    "order_id": 12345
}))

# Subscriber
pubsub = redis.pubsub()
pubsub.subscribe("notifications")
for message in pubsub.listen():
    handle_notification(message)

Padrão 5: Locks distribuídos

Evite condições de corrida em múltiplos servidores:

# Adquirir lock (NX = somente se não existir, EX = expirar)
SET lock:invoice:gen "worker-1" NX EX 30

# Liberar lock (somente se você for o dono — usar script Lua)

Redis Streams — event sourcing e filas de mensagens

Streams são a resposta do Redis a logs de eventos no estilo Kafka:

# Adicionar eventos
XADD orders * user_id 1001 product "Widget" qty 3
XADD orders * user_id 1002 product "Gadget" qty 1

# Ler os eventos mais recentes
XRANGE orders - + COUNT 10

# Consumer groups (múltiplos workers)
XGROUP CREATE orders processors $ MKSTREAM
XREADGROUP GROUP processors worker-1 COUNT 5 BLOCK 2000 STREAMS orders >

Streams oferecem logs de eventos persistentes e reproduzíveis com consumer groups — perfeitos para comunicação entre microsserviços.

Dicas de performance

1. Use pipelining para operações em massa

Em vez de 100 viagens de ida e volta, envie 100 comandos de uma vez:

pipe = redis.pipeline()
for i in range(100):
    pipe.set(f"key:{i}", f"value:{i}")
pipe.execute()  # Uma única viagem de ida e volta

2. Escolha a estrutura de dados correta

  • Precisa de um contador? → INCR (não GET + SET)
  • Precisa de um objeto? → Hash (não string JSON serializada)
  • Precisa de itens únicos? → Set (não List + lógica de dedup)
  • Precisa de ranking? → Sorted Set (não Sort após consulta)

3. Defina TTL em tudo

A memória é finita. Sempre defina expiração nas chaves de cache:

SET cache:api:result "{...}" EX 300   # TTL de 5 minutos

4. Use convenções de nomenclatura para chaves

recurso:id:campo
user:1001:profile
cache:api:v2:products
session:abc123
queue:emails:pending

Persistência: RDB vs AOF

O Redis pode persistir dados em disco para garantir durabilidade:

Modo Como funciona Trade-off
RDB Snapshots point-in-time Recuperação rápida, possível perda de dados
AOF Registra cada operação de escrita Mais lento, perda mínima de dados
Ambos RDB + AOF combinados Melhor durabilidade
# Em redis.conf
save 900 1          # Snapshot se 1 chave mudou em 900s
appendonly yes      # Habilitar AOF
appendfsync everysec  # Fsync a cada segundo

Referência rápida de comandos

Precisa consultar um comando específico do Redis? Use nosso Redis Cheat Sheet — cobre mais de 200 comandos organizados por estrutura de dados, com sintaxe, exemplos e cópia com um clique.

Conclusão

O Redis é muito mais do que um cache. Suas estruturas de dados se encaixam perfeitamente nas necessidades reais das aplicações:

  • Strings → sessões, contadores, cache simples
  • Hashes → perfis de usuário, objetos de configuração
  • Lists → filas, feeds de atividade, itens recentes
  • Sets → tags, visitantes únicos, relacionamentos
  • Sorted Sets → leaderboards, rankings, filas de prioridade
  • Streams → event sourcing, filas de mensagens
  • Pub/Sub → notificações em tempo real, chat

Comece com o cache, mas não pare por aí. O Redis pode substituir várias peças de infraestrutura na sua stack — e fazer tudo isso com velocidade abaixo de milissegundo.