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.
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.