kb.erickguedes.com
Redis: Cache, Fila e Tempo Real

Produção, Tuning e Segurança

Aula 6 de 6

Persistence

RDB (Snapshot)

# redis.conf — RDB
save 900 1               # snapshot a cada 15 min se >= 1 chave alterada
save 300 10              # snapshot a cada 5 min se >= 10 chaves
save 60 10000            # snapshot a cada 1 min se >= 10000 chaves

stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
rdb-del-sync-files no
dbfilename dump.rdb
dir /var/lib/redis

# Comandos manuais
BGSAVE                   # snapshot em background
SAVE                     # snapshot bloqueante (não usar em produção)
LASTSAVE                 # timestamp do último snapshot

AOF (Append Only File)

# redis.conf — AOF
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec      # fsync a cada 1s (equilíbrio)

# Opções de fsync:
# always: fsync a cada comando (mais seguro, mais lento)
# everysec: fsync a cada 1s (recomendado)
# no: deixa para o SO (mais rápido, menos seguro)

# Rewrite (compactação)
auto-aof-rewrite-percentage 100   # rewrite quando AOF dobra de tamanho
auto-aof-rewrite-min-size 64mb    # mínimo para fazer rewrite

# Comando manual
BGREWRITEAOF

# Segurança ao carregar AOF
aof-load-truncated yes    # carregar AOF truncado (último comando incompleto)
aof-use-rdb-preamble yes  # RDB + AOF hybrid (Redis 7.4+)

RDB + AOF Hybrid (Redis 7.4+)

# Redis 7.4+ combina RDB e AOF
aof-use-rdb-preamble yes

# Durante BGREWRITEAOF, Redis cria:
# 1. Um snapshot RDB (dados até o momento)
# 2. Comandos incrementais em AOF

# Vantagens:
# - Carregamento mais rápido que AOF puro
# - Menor perda de dados que RDB puro
# - Tamanho menor que AOF puro

# ⚠️ IMPORTANTE: Redis 7.4 introduziu:
#   - aof-use-rdb-preamble como padrão yes
#   - AOF sempre carrega RDB primeiro, depois aplica AOF

Sentinel

Configuração

# /etc/redis/sentinel.conf
port 26379
daemonize no
pidfile /var/run/redis-sentinel.pid
logfile /var/log/redis/sentinel.log

# Monitorar o master
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1

# Autenticação
sentinel auth-pass mymaster SuaSenhaAqui

# Notificações
sentinel notification-script mymaster /etc/redis/notify.sh

Três instâncias Sentinel

# Executar 3 sentinels (mínimo para quorum)
redis-sentinel /etc/redis/sentinel-1.conf
redis-sentinel /etc/redis/sentinel-2.conf
redis-sentinel /etc/redis/sentinel-3.conf

# Verificar status
redis-cli -p 26379 SENTINEL masters
redis-cli -p 26379 SENTINEL slaves mymaster
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
redis-cli -p 26379 SENTINEL ckquorum mymaster  # verificar quorum

# Simular failover
redis-cli -p 26379 SENTINEL failover mymaster

Client Awareness (Redis 7.2+)

# Clientes modernos podem descobrir o master via sentinel
# Exemplo Node.js (ioredis):
# const Redis = require('ioredis');
# const redis = new Redis({
#     sentinels: [
#         { host: 'sentinel1', port: 26379 },
#         { host: 'sentinel2', port: 26379 },
#         { host: 'sentinel3', port: 26379 }
#     ],
#     name: 'mymaster'
# });

Cluster

Configuração

# redis.conf — Cluster (mínimo 3 masters, 3 réplicas)
port 7000
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 5000
appendonly yes

Criar Cluster

# Iniciar 6 instâncias
redis-server redis-7000.conf
redis-server redis-7001.conf
redis-server redis-7002.conf
redis-server redis-7003.conf
redis-server redis-7004.conf
redis-server redis-7005.conf

# Criar cluster (3 masters + 3 réplicas)
redis-cli --cluster create \
    127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
    127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
    --cluster-replicas 1

# Hash slots: 0-16383 distribuídos entre os 3 masters
# Cada master tem 5461-5462 slots

Operações no Cluster

# Informações
redis-cli -p 7000 CLUSTER INFO
redis-cli -p 7000 CLUSTER NODES
redis-cli -p 7000 CLUSTER SLOTS

# Contar keys por nó
redis-cli -p 7000 CLUSTER COUNTKEYSINSLOT 0   # keys em slot específico
redis-cli -p 7000 DBSIZE

# Adicionar nó
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000

# Adicionar como réplica
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave

# Remover nó
redis-cli --cluster del-node 127.0.0.1:7006 <node-id>

# Rebalancear slots
redis-cli --cluster rebalance 127.0.0.1:7000

Resharding

# Mover slots de um nó para outro
redis-cli --cluster reshard 127.0.0.1:7000

# Modo não interativo:
# Mover 1000 slots do nó source para o nó target
redis-cli --cluster reshard 127.0.0.1:7000 \
    --cluster-from <source-node-id> \
    --cluster-to <target-node-id> \
    --cluster-slots 1000 \
    --cluster-yes

# Verificar distribuição
redis-cli --cluster check 127.0.0.1:7000

Movimentação de Dados

# Quando um cliente acessa um nó que não tem o slot:
# MOVED: redirecionamento permanente (hash slot movido)
# ASK: redirecionamento temporário (resharding em andamento)

# Cliente recebe:
# -MOVED 1234 127.0.0.1:7001
# Deve reenviar para o nó correto e atualizar rota

# -ASK 1234 127.0.0.1:7001
# Deve enviar ASKING antes do comando, depois reenviar

Monitoring

# INFO — visão geral
redis-cli INFO

# Seções específicas
redis-cli INFO server
redis-cli INFO clients
redis-cli INFO memory
redis-cli INFO persistence
redis-cli INFO stats
redis-cli INFO replication
redis-cli INFO cluster
redis-cli INFO keyspace

# Métricas importantes no INFO:
# instantaneous_ops_per_sec: comandos/s atuais
# instantaneous_input_kbps: entrada KB/s
# instantaneous_output_kbps: saída KB/s
# hit_rate: keyspace_hits / (keyspace_hits + keyspace_misses)
# connected_clients
# blocked_clients
# used_memory_rss / used_memory (fragmentation)

SLOWLOG

# Comandos lentos
CONFIG SET slowlog-log-slower-than 10000  # 10ms (microssegundos)
CONFIG SET slowlog-max-len 1000

# Ver log
SLOWLOG GET
SLOWLOG GET 5          # últimos 5 comandos lentos
SLOWLOG LEN            # total de entradas
SLOWLOG RESET          # limpar log

# Exemplo de saída:
# 1) 1) (integer) 123      # ID
#    2) (integer) 1717000  # timestamp
#    3) (integer) 25000    # duração (microssegundos)
#    4) 1) "KEYS"          # comando
#       2) "*"
#    5) "127.0.0.1:6379"   # cliente

MONITOR (Apenas Debug)

# ⚠️ MONITOR: APENAS PARA DEBUG!
# Reduz performance em ~50%, NÃO usar em produção

redis-cli MONITOR
# 1717000000.123456 [0 127.0.0.1:6379] "SET" "key" "value"
# 1717000000.123457 [0 127.0.0.1:6379] "GET" "key"

# Alternativa segura: INFO stats + SLOWLOG

redis-benchmark e Latency

# Benchmark básico
redis-benchmark -q -n 100000 -c 50
# -q: quiet (apenas resumo)
# -n 100000: 100k requisições
# -c 50: 50 conexões paralelas

# Testar comandos específicos
redis-benchmark -t SET,GET,Lpush -q

# Testar pipeline
redis-benchmark -P 16 -q

# Latency Doctor (Redis 7.2+)
redis-cli --latency          # latência contínua
redis-cli --latency-history  # histórico a cada 15s
redis-cli --latency-dist     # distribuição (gráfico)
redis-cli --intrinsic-latency 100  # latência intrínseca do sistema

Security

ACL — Access Control Lists (Redis 6+)

# Listar usuários
ACL LIST
# user default on nopass ~* +@all

# Criar usuário
ACL SETUSER app_user on >senha123 ~* +@all -@dangerous

# Usuário com permissões específicas
ACL SETUSER read_only on >read123 ~* +@read +@pubsub

# Usuário restrito a keys específicas
ACL SETUSER cache_user on >cache123 ~cache:* +@read +@write

# Categorias de comandos
# @read: GET, MGET, HGET, etc.
# @write: SET, DEL, EXPIRE, etc.
# @admin: CONFIG, SHUTDOWN, DEBUG, etc.
# @dangerous: FLUSHALL, DEBUG, CONFIG SET, etc.
# @fast: comandos O(1)
# @slow: comandos O(N) como KEYS
# @pubsub: PUBLISH, SUBSCRIBE, etc.
# @connection: AUTH, PING, QUIT, etc.

# Usuário sem comandos perigosos
ACL SETUSER app_user2 on >appP@ss ~app:* +@all -@admin -@dangerous

# Gerar ACL file
ACL SAVE

# ACL log
ACL LOG
ACL LOG RESET

# Verificar permissões
ACL GETUSER app_user

SSL/TLS

# redis.conf — TLS
tls-port 6380
port 0                        # desabilitar porta não-TLS

tls-cert-file /etc/redis/certs/redis.crt
tls-key-file /etc/redis/certs/redis.key
tls-ca-cert-file /etc/redis/certs/ca.crt
tls-auth-clients yes
tls-protocols "TLSv1.2 TLSv1.3"
tls-ciphers "EECDH+AESGCM:EDH+AESGCM"
tls-prefer-server-ciphers yes

# Conectar com TLS
redis-cli --tls \
    --cert /etc/redis/certs/client.crt \
    --key /etc/redis/certs/client.key \
    --cacert /etc/redis/certs/ca.crt

Comandos Perigosos — Rename

# redis.conf — desabilitar/renomear comandos perigosos
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""
rename-command SHUTDOWN ""
rename-command DEBUG ""
rename-command SCRIPT ""

# Ou renomear para dificultar:
rename-command FLUSHALL "5f4dcc3b5aa765d61d8327deb882cf99"
rename-command DEBUG "ea6a78593e3c7b44d75ec9b12c23b7f3"

Memory Optimization

# Ver fragmentação
redis-cli INFO memory | grep mem_fragmentation_ratio
# > 1.5: fragmentação alta (pode precisar restart ou MEMORY PURGE)
# < 1.0: swapping (perigoso!)

# Ver memória usada por chave
redis-cli MEMORY USAGE key_name
redis-cli MEMORY STATS
redis-cli MEMORY DOCTOR   # diagnóstico e recomendações

# Otimizações
# 1. Usar Hash ao invés de String para objetos
#    user:1001:name + user:1001:email  →  HSET user:1001 name "Maria" email...
#    Economia: ~50% para objetos com 5+ campos

# 2. Usar inteiros ao invés de strings
#    SET age 30 ao invés de SET age "30"

# 3. Usar TTL para dados temporários
#    SET temp:data "..." EX 300

# 4. Escolher encoding apropriado
#    list-max-ziplist-size  -2   # -2 = 8KB max per node
#    hash-max-listpack-entries 512
#    hash-max-listpack-value 64
#    set-max-intset-entries 512
#    zset-max-listpack-entries 128
#    zset-max-listpack-value 64

Kernel Tuning

# /etc/sysctl.conf — ajustes para Redis em produção

# Overcommit memory (essencial para BGSAVE)
vm.overcommit_memory = 1

# Desabilitar Transparent Huge Pages (THP)
echo never > /sys/kernel/mm/transparent_hugepage/enabled

# Networking
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
vm.swappiness = 1            # evitar swap

# Aplicar
sysctl -p

# THP systemd service (persistente)
# Criar /etc/systemd/system/disable-thp.service
# [Unit]
# Description=Disable Transparent Huge Pages
#
# [Service]
# Type=oneshot
# ExecStart=/bin/sh -c "echo never > /sys/kernel/mm/transparent_hugepage/enabled"
# RemainAfterExit=yes
#
# [Install]
# WantedBy=multi-user.target

Lab: Cluster Redis em Docker Compose

cat > docker-compose.yml << 'YAML'
version: '3.8'

services:
  redis-master-1:
    image: redis:7-alpine
    ports: ["7001:6379"]
    volumes: ["./conf/redis-1.conf:/usr/local/etc/redis/redis.conf"]
    command: redis-server /usr/local/etc/redis/redis.conf
    networks: [redis-cluster]

  redis-master-2:
    image: redis:7-alpine
    ports: ["7002:6379"]
    volumes: ["./conf/redis-2.conf:/usr/local/etc/redis/redis.conf"]
    command: redis-server /usr/local/etc/redis/redis.conf
    networks: [redis-cluster]

  redis-master-3:
    image: redis:7-alpine
    ports: ["7003:6379"]
    volumes: ["./conf/redis-3.conf:/usr/local/etc/redis/redis.conf"]
    command: redis-server /usr/local/etc/redis/redis.conf
    networks: [redis-cluster]

  redis-replica-1:
    image: redis:7-alpine
    ports: ["7004:6379"]
    volumes: ["./conf/redis-4.conf:/usr/local/etc/redis/redis.conf"]
    command: redis-server /usr/local/etc/redis/redis.conf
    depends_on: [redis-master-1, redis-master-2, redis-master-3]
    networks: [redis-cluster]

  redis-replica-2:
    image: redis:7-alpine
    ports: ["7005:6379"]
    volumes: ["./conf/redis-5.conf:/usr/local/etc/redis/redis.conf"]
    command: redis-server /usr/local/etc/redis/redis.conf
    depends_on: [redis-master-1, redis-master-2, redis-master-3]
    networks: [redis-cluster]

  redis-replica-3:
    image: redis:7-alpine
    ports: ["7006:6379"]
    volumes: ["./conf/redis-6.conf:/usr/local/etc/redis/redis.conf"]
    command: redis-server /usr/local/etc/redis/redis.conf
    depends_on: [redis-master-1, redis-master-2, redis-master-3]
    networks: [redis-cluster]

  cluster-init:
    image: redis:7-alpine
    depends_on:
      - redis-master-1
      - redis-master-2
      - redis-master-3
      - redis-replica-1
      - redis-replica-2
      - redis-replica-3
    networks: [redis-cluster]
    entrypoint: >
      sh -c "
        sleep 5 &&
        redis-cli --cluster create
          172.20.0.2:6379 172.20.0.3:6379 172.20.0.4:6379
          172.20.0.5:6379 172.20.0.6:6379 172.20.0.7:6379
          --cluster-replicas 1 --cluster-yes
      "

  sentinel-1:
    image: redis:7-alpine
    ports: ["26379:26379"]
    volumes: ["./sentinel/sentinel-1.conf:/etc/redis/sentinel.conf"]
    command: redis-sentinel /etc/redis/sentinel.conf

  sentinel-2:
    image: redis:7-alpine
    ports: ["26380:26379"]
    volumes: ["./sentinel/sentinel-2.conf:/etc/redis/sentinel.conf"]
    command: redis-sentinel /etc/redis/sentinel.conf

  sentinel-3:
    image: redis:7-alpine
    ports: ["26381:26379"]
    volumes: ["./sentinel/sentinel-3.conf:/etc/redis/sentinel.conf"]
    command: redis-sentinel /etc/redis/sentinel.conf

networks:
  redis-cluster:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24
YAML

Lab: Diagnóstico de Produção

cat > redis-healthcheck.sh << 'SCRIPT'
#!/bin/bash
set -euo pipefail

echo "=== REDIS HEALTH CHECK $(date) ==="

# 1. Ping
echo ""
echo "─── PING ───"
redis-cli PING

# 2. Info básico
echo ""
echo "─── INFORMAÇÕES GERAIS ───"
redis-cli INFO server | grep -E "redis_version|uptime_in_seconds|os"
redis-cli INFO clients | grep -E "connected_clients|blocked_clients"

# 3. Memória
echo ""
echo "─── MEMÓRIA ───"
redis-cli INFO memory | grep -E \
    "used_memory_human|used_memory_rss_human|maxmemory_human|mem_fragmentation_ratio"
redis-cli MEMORY DOCTOR 2>/dev/null | head -5 || echo "MEMORY DOCTOR unavailable"

# 4. Performance
echo ""
echo "─── PERFORMANCE ───"
redis-cli INFO stats | grep -E \
    "instantaneous_ops_per_sec|total_commands_processed|keyspace_hits|keyspace_misses"

# 5. Comandos lentos
echo ""
echo "─── SLOW LOG ───"
redis-cli SLOWLOG LEN
redis-cli SLOWLOG GET 3

# 6. Keyspace
echo ""
echo "─── KEYSPACE ───"
redis-cli INFO keyspace
redis-cli DBSIZE

# 7. Replicação (se cluster/sentinel)
echo ""
echo "─── REPLICAÇÃO ───"
redis-cli INFO replication | grep -E "role|connected_slaves|master_host|master_link_status" || true

# 8. Latência
echo ""
echo "─── LATÊNCIA (3s) ───"
redis-cli --latency -h localhost -p 6379 &
PID=$!
sleep 3
kill $PID 2>/dev/null || true
wait $PID 2>/dev/null || true

# 9. ACL (se configurado)
echo ""
echo "─── ACL ───"
redis-cli ACL LIST 2>/dev/null | head -3 || echo "ACL not configured"

# 10. Diagnóstico final
echo ""
echo "─── FIM ───"
FRAG=$(redis-cli INFO memory | grep mem_fragmentation_ratio | awk -F: '{print $2}' | tr -d '\r')
if (( $(echo "$FRAG > 1.5" | bc -l) )); then
    echo "⚠️  Fragmentação alta: $FRAG (considere restart)"
elif (( $(echo "$FRAG < 1.0" | bc -l) )); then
    echo "❌ Swapping detectado: $FRAG (aumente RAM ou ajuste maxmemory)"
else
    echo "✅ Fragmentação saudável: $FRAG"
fi
SCRIPT

chmod +x redis-healthcheck.sh

RDB + AOF hybrid (Redis 7.4+) é o padrão de persistência recomendado. Sentinel para HA, Cluster para escala horizontal. INFO + SLOWLOG para monitoramento. ACL para segurança granular. rename-command para desabilitar comandos perigosos. THP deve ser desabilitado. vm.overcommit_memory = 1 é obrigatório.