Estudo de Caso Técnico: Como Detectamos e Corrigimos um Estrangulamento Silencioso no Redis que Ameaçava uma Black Friday

Carregando...
Imagem do estudo de caso da HEX: detecção de gargalo no Redis e correção que garantiu a estabilidade de um e-commerce na Black Friday, preservando R$ 200 mil em receita.
Estudo de caso real: como um estrangulamento silencioso no Redis ameaçava uma Black Friday. Aprenda o script usado, as correções aplicadas e como evitamos um prejuízo de R$ 200 mil.

"O site está lento nas promoções. Já aumentamos o servidor, mas não adianta."

Essa frase, que ouvimos de um grande e-commerce semanas antes da Black Friday, é mais comum do que parece. E, na maioria das vezes, aumentar servidor é a pior decisão possível — porque mascara o problema real enquanto aumenta a conta do cliente.

Neste post técnico, vou abrir o passo a passo completo da investigação que fizemos, incluindo:

✅ As métricas exatas que monitoramos
✅ O script que usamos para detectar o padrão anômalo
✅ A correção cirúrgica que aplicamos
✅ Como validamos que o problema não voltaria

Prepare-se para ir além do "achismo" e entrar no mundo da arquitetura baseada em dados.


O Cenário: Diagnóstico Inicial

Cliente: E-commerce de moda (nome preservado por acordo de confidencialidade) Tráfego médio: 50 mil visitas/dia Pico esperado (Black Friday): 250 mil visitas/dia Problema reportado: "Site fica lento e eventualmente cai nos primeiros 10 minutos de promoções relâmpago"

O que já tinham feito antes de nos chamar:

  • Dobraram a capacidade do servidor (mais CPU, mais RAM)
  • Contrataram CDN adicional
  • Instalaram 2 módulos de cache full page

Resultado: O problema persistia. O cliente estava prestes a cancelar a Black Friday por medo de prejuízo.


Fase 1: Estabelecendo o "Batimento Cardíaco"

Nossa primeira ação foi parar de apagar incêndio e começar a coletar dados.

Implementamos monitoramento granular com coleta a cada 10 segundos durante 72 horas, incluindo períodos sem promoção.

O que descobrimos na linha de base:

Métrica Valor normal Durante lentidão Diferença
CPU 35% 92% +57%
Redis hit rate 94% 43% -51%
Slow queries/min 2 47 +45
Conexões MySQL 120 890 +770

Primeira pista: A CPU disparava, mas não por falta de capacidade — ela disparava porque o Redis parava de responder, e as requisições caiam no banco de dados.


Fase 2: Investigação Direcionada ao Redis

Script de Análise em Tempo Real

Desenvolvemos um script simples mas poderoso para monitorar o Redis durante os picos:

#!/bin/bash
# redis-autopsy.sh - Monitoramento detalhado do Redis

while true; do
  TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")

  # Métricas críticas
  REDIS_INFO=$(redis-cli INFO all)

  USED_MEMORY=$(echo "$REDIS_INFO" | grep "used_memory_human" | cut -d':' -f2)
  HIT_RATE=$(echo "$REDIS_INFO" | grep "keyspace_hits" -A 2 | awk 'NR==1{hits=$2} NR==2{misses=$2} END{if (hits+misses>0) print (hits/(hits+misses))*100; else print 0}')
  CONNECTED_CLIENTS=$(echo "$REDIS_INFO" | grep "connected_clients" | cut -d':' -f2)
  EVICTED_KEYS=$(echo "$REDIS_INFO" | grep "evicted_keys" | cut -d':' -f2)

  # Log formatado
  echo "$TIMESTAMP | Mem: $USED_MEMORY | Hit: ${HIT_RATE}% | Clients: $CONNECTED_CLIENTS | Evicted: $EVICTED_KEYS" >> redis-baseline.log

  # Se hit rate cair abaixo de 80%, log detalhado
  if (( $(echo "$HIT_RATE < 80" | bc -l) )); then
    echo "--- ALERTA EM $TIMESTAMP ---" >> redis-critical.log
    redis-cli --stat >> redis-critical.log
    echo "Top 5 slow queries no momento:" >> redis-critical.log
    mysql -e "SELECT * FROM mysql.slow_log ORDER BY query_time DESC LIMIT 5;" >> redis-critical.log
  fi

  sleep 10
done

O Momento "Eureka"

Durante um pico de teste (promoção relâmpago controlada), o script capturou isso:

2025-10-15 14:23:10 | Mem: 1.8G/2G | Hit: 94% | Clients: 120 | Evicted: 0
2025-10-15 14:23:20 | Mem: 1.9G/2G | Hit: 92% | Clients: 180 | Evicted: 0
2025-10-15 14:23:30 | Mem: 2.0G/2G | Hit: 65% | Clients: 350 | Evicted: 1200
2025-10-15 14:23:40 | Mem: 1.7G/2G | Hit: 43% | Clients: 890 | Evicted: 4500

Padrão identificado:

  1. Redis atingia 100% da memória
  2. Política volatile-lru começava a remover chaves expiráveis
  3. Como as chaves de sessão não expiravam corretamente, sobravam poucas chaves removíveis
  4. Redis entrava em modo "sobrevivência" e derrubava tudo
  5. Requisições iam para o MySQL, que não aguentava o volume

Fase 3: Descoberta da Causa Raiz

Agora sabíamos o que acontecia. Precisávamos saber por quê.

Análise do Padrão de Chaves no Redis

# Conectando no Redis para inspecionar
redis-cli --bigkeys

# Resultado resumido
# -------- summary -------
# Sampled 1.2M keys in keyspace
# Total key length in bytes: 89M
# Biggest string found 'sess_9a8b7c6d5e' has 48572 bytes
# Biggest set found 'catalog_products' has 2340 members
# Biggest hash found 'customer_12345' has 89 fields

# Listando sessões ativas
redis-cli KEYS "sess_*" | wc -l
# Retorno: 450 mil sessões ativas

Descoberta chocante: Havia 450 mil sessões ativas no Redis, mesmo com apenas 1.200 usuários ativos no momento.

A Investigação no Código

Examinamos o módulo de checkout que o cliente usava e encontramos:

// Módulo de terceiros - vendor/CheckoutOptimizer/Model/Session.php

public function extendSession($customerId)
{
    // Código problemático: cria sessão sem tempo de expiração
    $this->session->setData('customer_'.$customerId, $this->getCartData());
    // Linha faltando: $this->session->setExpiry(3600);
}

Problema: O módulo criava sessões permanentes para cada interação no carrinho. Com 50 mil visitas/dia, em 1 mês eram 1.5 milhão de sessões acumuladas. O Redis lotava e morria.


Fase 4: A Solução Cirúrgica

Correção 1: Política de Expiração no Redis

Ajuste imediato na configuração do Redis:

# /etc/redis/redis.conf
maxmemory 4gb  # Aumentamos de 2gb para 4gb (solução temporária)
maxmemory-policy allkeys-lru  # Mudamos de volatile-lru
maxmemory-samples 10

Por que allkeys-lru? Agora o Redis considera todas as chaves para remoção quando a memória acaba. Sessões antigas são removidas automaticamente para dar espaço às novas.

Correção 2: Fix no Módulo (e alternativas)

// Correção aplicada no módulo
public function extendSession($customerId)
{
    $sessionKey = 'customer_'.$customerId;
    $this->session->setData($sessionKey, $this->getCartData());
    // Adicionamos expiração de 1 hora
    $this->session->setExpiry(3600);

    // Log para monitoramento
    $this->logger->info("Session created for customer $customerId with 1h expiry");
}

Alternativa sem modificar o módulo: Criamos um script de limpeza programada:

#!/bin/bash
# cleanup-old-sessions.sh - Remove sessões antigas do Redis
# Rodar a cada 30 minutos via crontab

MAX_SESSION_AGE=3600  # 1 hora em segundos
CURRENT_TIME=$(date +%s)

redis-cli KEYS "customer_*" | while read key; do
    # Extrai timestamp da chave (se existir no padrão)
    # Implementação simplificada - na prática usamos Redis TTL
    TTL=$(redis-cli TTL "$key")
    if [ $TTL -eq -1 ]; then
        # Chave sem expiração - força expiração
        redis-cli EXPIRE "$key" 3600
        echo "Fixed key without expiry: $key"
    fi
done

Correção 3: Query Otimizada

O módulo de desconto executava esta query a cada requisição:

-- Query problemática (executada 10x por requisição)
SELECT * FROM salesrule_coupon 
WHERE coupon_code = 'TEMPORARIO' 
AND is_active = 1 
AND (from_date IS NULL OR from_date <= NOW())
AND (to_date IS NULL OR to_date >= NOW());

Problema: Sem índice em coupon_code e com função NOW() na condição, a query varria a tabela inteira a cada execução.

Otimização aplicada:

-- Adicionar índice
ALTER TABLE salesrule_coupon ADD INDEX idx_coupon_code_active (coupon_code, is_active);

-- Query refatorada (pré-processando as datas no PHP)
$now = date('Y-m-d H:i:s');
$query = "SELECT * FROM salesrule_coupon 
          WHERE coupon_code = :code 
          AND is_active = 1 
          AND (from_date IS NULL OR from_date <= :now)
          AND (to_date IS NULL OR to_date >= :now)";

Resultado: Tempo de execução caiu de 450ms para 12ms.


Fase 5: Validação e Resultados

Teste de Carga Pré-Black Friday

Simulamos 300% do tráfego esperado usando K6:

// k6-script.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export let options = {
    stages: [
        { duration: '5m', target: 500 },  // Ramp up
        { duration: '10m', target: 5000 }, // Pico
        { duration: '5m', target: 0 },     // Ramp down
    ],
};

export default function() {
    let res = http.get('https://cliente.com.br/promocao');
    check(res, {
        'status é 200': (r) => r.status === 200,
        'tempo < 500ms': (r) => r.timings.duration < 500,
    });
    sleep(1);
}

Resultados comparativos:

Métrica Antes Depois Melhoria
Tempo médio de resposta 2.3s 340ms 85%
Redis hit rate durante pico 43% 97% 54pp
Erros 5xx 12% 0.2% 98%
Conexões MySQL simultâneas 890 145 84%

Na Black Friday (Dia D)

  • Pico de tráfego real: 280 mil visitas (12% acima da projeção)
  • Tempo de resposta médio: 380ms
  • Zero indisponibilidade
  • Receita estimada preservada: R$ 200 mil+ (cálculo baseado em ticket médio x conversão)

O CTO do cliente nos mandou uma mensagem às 15h da Black Friday: "Primeira Black Friday em 5 anos que não preciso ficar olhando o servidor. Simplesmente funciona."


Conclusão: Lições Aprendidas

Este caso prova que performance não se compra com hardware — se conquista com investigação cirúrgica.

Para replicar no seu projeto:

  1. Monitore o Redis como se fosse seu paciente – hit rate baixo é sintoma, não causa
  2. Desconfie de módulos que "otimizam tudo" – eles geralmente introduzem mais problemas do que resolvem
  3. Crie scripts de auto-diagnóstico – o script redis-autopsy.sh que compartilhei pode ser adaptado para seu caso
  4. Teste sob carga real – simule picos antes de eventos críticos

Este caso gerou debate no LinkedIn

Quando compartilhei a versão resumida deste estudo de caso no LinkedIn, vários devs comentaram sobre "vilões silenciosos" que já encontraram.

Leia a discussão e compartilhe sua experiência:

Me segue no LinkedIn para acompanhar novos cases e conteúdos sobre arquitetura preditiva em Magento.


Quer uma análise preditiva completa para seu e-commerce antes do próximo grande evento? https://hexcommerce.com.br/contacts

Author: Alberto Braschi

Alberto Braschi

Fundador na HEX | Magento • Adobe Commerce • Arquitetura Preditiva

Alberto Braschi é fundador da HEX E-commerce Solutions, consultoria especializada em Magento e Adobe Commerce. Com mais de uma década de experiência em arquitetura de software, ajuda empresas a migrarem do caos reativo para operações preditivas e de alta performance.

Related posts

^ © 2025. Todos os direitos reservados. HEX E-commerce Solutions