Solucionando Comandos Lentos no Redis: Um Checklist de Desempenho
Um checklist prático para encontrar comandos lentos no Redis com SLOWLOG, MONITOR, ferramentas de latência, complexidade de comandos e correções mais seguras.
Solucionando Comandos Lentos no Redis: Um Checklist de Desempenho
Comandos lentos no Redis geralmente começam como comandos normais que superam suas suposições. Uma chamada SMEMBERS era inofensiva quando o conjunto tinha 200 membros. Uma consulta de dashboard estava bem quando carregava 50 chaves. Um script Lua era rápido até que um cliente criou uma forma de dados muito maior que todos os outros.
A pergunta útil não é apenas "qual comando está lento?" É "qual forma de dados tornou este comando lento, e por que a aplicação está pedindo ao Redis para fazer tanto trabalho de uma só vez?"
Entendendo o Desempenho do Redis
O desempenho do Redis é geralmente excepcional devido à sua natureza em memória. No entanto, vários fatores podem contribuir para a latência dos comandos:
- Complexidade do Comando: Certos comandos são inerentemente mais intensivos em recursos do que outros (ex.:
KEYSem um grande conjunto de dados vs.GET). - Tamanho e Estrutura dos Dados: Listas grandes, conjuntos ou conjuntos ordenados, ou estruturas de dados complexas, podem impactar o desempenho dos comandos que operam sobre eles.
- Latência de Rede: Embora não seja diretamente um problema de comando, alta latência de rede entre o cliente e o servidor pode fazer com que os comandos pareçam lentos.
- Carga do Servidor: Alto uso de CPU, memória insuficiente ou outros processos no servidor Redis podem degradar o desempenho.
- Comandos Bloqueantes: Certas operações podem bloquear o loop de eventos do Redis, afetando todos os comandos subsequentes.
Identificando Comandos Lentos com SLOWLOG
O comando SLOWLOG é o mecanismo embutido do Redis para registrar comandos que excedem um tempo de execução especificado. Esta é sua principal ferramenta para identificar proativamente comandos problemáticos.
Como o SLOWLOG Funciona
O Redis mantém um buffer circular que armazena informações sobre comandos que levaram mais tempo do que o limite configurado slowlog-log-slower-than (em microssegundos). O limite padrão é tipicamente 10 milissegundos (10000 microssegundos). Quando este buffer enche, as entradas mais antigas são descartadas.
Subcomandos Chave do SLOWLOG
SLOWLOG GET [count]: Recupera as últimascountentradas do log lento. Secountfor omitido, recupera todas as entradas.SLOWLOG LEN: Retorna o comprimento atual do log lento (número de entradas).SLOWLOG RESET: Limpa as entradas do log lento. Use este comando com cautela, pois ele remove permanentemente os dados registrados.
Exemplo de Uso do SLOWLOG
Vamos supor que você suspeita que alguns comandos estão demorando muito. Você pode verificar o log lento da seguinte forma:
# Conecte-se à sua instância Redis
redis-cli
# Obtenha os últimos 5 comandos lentos
127.0.0.1:6379> SLOWLOG GET 5
A saída será algo como:
1) 1) (integer) 18
2) (integer) 1678886400
3) (integer) 15000
4) 1) "KEYS"
2) "*"
2) 1) (integer) 17
2) (integer) 1678886390
3) (integer) 12000
4) 1) "SMEMBERS"
2) "my_large_set"
...
Explicação da saída:
- ID da Entrada: Um identificador único para a entrada do log lento.
- Timestamp: O timestamp Unix quando o comando foi executado.
- Tempo de Execução: A duração (em microssegundos) que o comando levou para executar.
- Comando e Argumentos: O próprio comando e seus argumentos.
No exemplo acima, KEYS * levou 15000 microssegundos (15ms) e SMEMBERS my_large_set levou 12000 microssegundos (12ms). Estes seriam considerados lentos se seu slowlog-log-slower-than estiver definido para 10000 microssegundos.
Configurando slowlog-log-slower-than
Você pode alterar dinamicamente o limite slowlog-log-slower-than usando o comando CONFIG SET:
127.0.0.1:6379> CONFIG SET slowlog-log-slower-than 50000 # Registra comandos mais lentos que 50ms
Para tornar esta alteração persistente entre reinicializações do Redis, você precisaria modificar o arquivo redis.conf e reiniciar o servidor Redis, ou usar CONFIG REWRITE para salvar as alterações no arquivo de configuração.
Monitoramento de Comandos em Tempo Real com MONITOR
Enquanto o SLOWLOG fornece uma visão histórica, o MONITOR oferece um fluxo em tempo real de todos os comandos sendo executados pelo servidor Redis. Isso é inestimável para depuração durante um período específico de desempenho lento ou para entender os padrões de tráfego de comandos.
Como o MONITOR Funciona
Quando você ativa o MONITOR, o Redis envia uma resposta ao cliente MONITOR para cada comando que recebe e processa. Isso pode gerar um volume muito alto de saída, especialmente em instâncias Redis ocupadas. Portanto, geralmente é recomendado usar o MONITOR com moderação e apenas quando estiver depurando ativamente.
Exemplo de Uso do MONITOR
A partir de uma sessão redis-cli separada, execute o comando MONITOR:
# Conecte-se à sua instância Redis em um terminal *separado*
redis-cli
# Inicie o monitoramento
127.0.0.1:6379> MONITOR
Agora, qualquer comando executado em outra sessão redis-cli ou pela sua aplicação aparecerá na saída do MONITOR. Por exemplo, se você executar SET mykey myvalue em outro cliente, verá:
1678887000.123456 [0 127.0.0.1:54321] "SET" "mykey" "myvalue"
Usando o MONITOR para Depuração
- Reproduza o Problema: Quando notar uma lentidão, inicie imediatamente o
MONITORem uma sessãoredis-clidedicada. - Dispare a Operação Lenta: Faça sua aplicação executar a ação que você suspeita estar causando a lentidão.
- Analise a Saída: Observe os comandos no fluxo do
MONITOR. Procure por:- Comandos que demoram muito para aparecer (embora o
MONITORem si não mostre o tempo de execução, você pode inferi-lo cronometrando os comandos manualmente ou observando atrasos). - Comandos incomuns ou inesperados sendo executados.
- Um alto volume de comandos que pode estar sobrecarregando o servidor.
- Comandos que demoram muito para aparecer (embora o
- Pare o Monitoramento: Pressione
Ctrl+Cpara sair do comandoMONITOR.
Importante: Não execute o MONITOR em um ambiente de produção por períodos prolongados, pois pode impactar significativamente o desempenho do Redis devido à sobrecarga de enviar cada comando ao cliente.
Causas Comuns de Comandos Lentos e Como Corrigi-las
Com base nas informações coletadas do SLOWLOG e MONITOR, aqui estão os culpados comuns e suas soluções:
1. Comando KEYS
- Problema: O comando
KEYSitera sobre todo o espaço de chaves para encontrar chaves que correspondem a um padrão. Em bancos de dados com milhões de chaves, isso pode levar muito tempo e bloquear o servidor Redis, afetando todos os outros clientes. - Solução: Evite
KEYSem grandes espaços de chaves de produção. UseSCANquando precisar de iteração incremental de chaves.SCANretorna um subconjunto de chaves correspondentes a um padrão em cada chamada, o que reduz a chance de bloquear o servidor por um longo período.
Você precisará chamar# Em vez de KEYS user:* redis-cli -h <host> -p <port> SCAN 0 MATCH user:* COUNT 100SCANvárias vezes, usando o cursor retornado pela chamada anterior, até que o cursor retorne a 0.
2. Scripts Complexos (Scripts Lua)
- Problema: Scripts Lua de longa duração ou ineficientes executados via
EVALouEVALSHApodem bloquear o servidor. Embora o Redis execute scripts atomicamente, um único script longo pode monopolizar o loop de eventos. - Solução: Otimize seus scripts Lua. Divida a lógica complexa em scripts menores e gerenciáveis. Analise o desempenho do script. Garanta que os loops dentro dos scripts sejam eficientes e terminem corretamente. Faça benchmark de seus scripts para entender seu tempo de execução.
3. Operações em Estruturas de Dados Grandes
- Problema: Comandos como
SMEMBERSem um conjunto com milhões de membros,LRANGEem uma lista muito longa, ouZRANGEem um conjunto ordenado enorme podem ser lentos. - Solução: Evite buscar estruturas de dados grandes inteiras. Em vez disso, use comandos iterativos ou processe dados em partes:
- Conjuntos: Use
SSCANem vez deSMEMBERS. - Listas: Use
LRANGEcom valoresstartestopmenores para recuperar dados em páginas. - Conjuntos Ordenados: Use
ZRANGEcomLIMITouZSCAN.
- Conjuntos: Use
4. Comandos que Exigem Iteração de Chaves (Menos Comum, mas Possível)
- Problema: Embora menos comum, comandos que podem iterar implicitamente sobre chaves devido à sua natureza podem ser lentos se o espaço de chaves for grande.
- Solução: Revise a referência de comandos do Redis para o comando específico e entenda sua complexidade. Considere estruturas de dados ou abordagens alternativas se um comando específico se mostrar um gargalo.
5. Comandos Bloqueantes (Raro no Redis Moderno)
- Problema: Versões antigas do Redis tinham alguns comandos que podiam bloquear o servidor. A maioria deles foi resolvida ou substituída.
- Solução: Certifique-se de estar usando uma versão recente do Redis. Consulte a documentação do Redis para quaisquer operações de bloqueio conhecidas específicas da sua versão.
Primeiro Decida se o Redis Está Lento ou o Cliente Está Esperando
Quando alguém diz "o Redis está lento", pode significar várias coisas diferentes. O servidor pode estar gastando muito tempo executando um comando. O cliente pode estar esperando na rede. Um pool de conexões pode estar esgotado. Um proxy TLS pode estar sobrecarregado. Uma resposta grande pode estar demorando mais para ser transferida do que o comando levou para executar.
SLOWLOG apenas registra o tempo de execução do comando dentro do Redis. Não inclui tempo de transferência de rede, tempo de fila do cliente ou tempo gasto esperando por uma conexão de um pool de aplicação. É por isso que um log lento limpo nem sempre prova que os usuários estão imaginando latência.
Compare três visões:
redis-cli --latency -h <host> -p <port>
redis-cli --latency-history -h <host> -p <port>
redis-cli SLOWLOG GET 10
Se a latência é alta mas o SLOWLOG está vazio, olhe para rede, pools de clientes, saturação de CPU do servidor, atividade de fork, persistência ou respostas grandes. Se o SLOWLOG mostra comandos caros repetidos, comece com o design do comando e da estrutura de dados.
Nas aplicações, adicione temporização em torno das chamadas Redis no limite do cliente. Registre a família do comando, o padrão da chave, o tempo decorrido e se o cliente esperou por uma conexão do pool. Não registre segredos ou payloads completos. Uma pequena quantidade de temporização estruturada geralmente responde se o atraso está dentro do Redis ou antes mesmo do comando chegar a ele.
Use a Complexidade do Comando como um Teste de Cheiro
Os comandos Redis são rápidos quando tocam uma quantidade pequena e limitada de dados. Eles se tornam arriscados quando varrem um grande espaço de chaves, retornam uma coleção enorme ou fazem trabalho proporcional a um valor grande.
Antes de culpar o hardware, verifique a complexidade do comando na referência de comandos do Redis para sua versão. Você não precisa memorizar todos os rótulos de complexidade, mas a forma importa:
GET user:123é limitado pelo tamanho de um valor.HGET profile:123 emailé limitado por uma busca em hash.SMEMBERS followers:celebrityretorna o conjunto inteiro.KEYS *varre todo o espaço de chaves.LRANGE queue 0 -1retorna a lista inteira.ZREMRANGEBYSCOREpode remover um grande número de membros de um conjunto ordenado.
O padrão arriscado é geralmente "me dê tudo." Pode funcionar por meses, depois falhar quando um conjunto cresce de centenas de membros para milhões. O Redis não ficou repentinamente lento; os dados cruzaram o ponto onde um comando ilimitado se tornou visível.
Substituições Mais Seguras para Padrões Lentos Comuns
Substitua comandos de espaço de chaves inteiro e coleção inteira por padrões incrementais.
Para descoberta de chaves, use SCAN:
redis-cli --scan --pattern 'user:*'
Para conjuntos, use SSCAN:
SSCAN active_users 0 COUNT 500
Para hashes, use HSCAN:
HSCAN user:123:settings 0 COUNT 200
Para conjuntos ordenados, prefira intervalos com limites explícitos:
ZRANGE leaderboard 0 99 WITHSCORES
ZRANGEBYSCORE events 1716600000 1716686400 LIMIT 0 500
Para listas, pagine com intervalos limitados:
LRANGE recent_jobs 0 99
SCAN é incremental, mas não é uma operação mágica gratuita. Pode retornar duplicatas e não fornece um snapshot perfeitamente consistente enquanto as chaves estão mudando. É bom para manutenção, migração e descoberta em segundo plano. Geralmente não é a primitiva certa para um caminho de requisição voltado ao usuário que precisa de uma lista precisa em tempo real.
Respostas Grandes Podem Ser o Custo Real
Um comando pode executar rapidamente e ainda prejudicar sua aplicação se retornar muitos dados. SMEMBERS em um conjunto enorme, HGETALL em um hash grande, ou MGET sobre milhares de valores grandes podem gastar tempo serializando a resposta e enviando-a pela rede. Esse custo pode não aparecer claramente apenas como tempo de execução do comando.
Observe a saída de rede e a memória do cliente durante a operação lenta. Se uma única requisição retorna dezenas ou centenas de megabytes, redesenhe o padrão de acesso. Armazene dados de resumo separadamente. Pagine o resultado. Use um índice de conjunto ordenado e busque apenas a fatia visível. Evite colocar documentos grandes no Redis quando a aplicação geralmente precisa de um campo.
Um exemplo prático: se um dashboard mostra os últimos 50 trabalhos, não armazene cada ID de trabalho em uma lista e chame LRANGE jobs 0 -1 antes de fatiar na aplicação. Armazene a lista em ordem do mais novo para o mais antigo e solicite apenas o que a página precisa:
LRANGE jobs:recent 0 49
Essa pequena mudança pode remover uma quantidade surpreendente de latência e pressão de memória.
MONITOR é um Bisturi, Não um Dashboard
MONITOR é útil quando você precisa ver exatamente quais comandos um cliente envia, especialmente quando suspeita que a aplicação está fazendo algo diferente do que a revisão de código sugere. Mas em um servidor Redis ocupado, MONITOR cria sobrecarga e produz uma enxurrada de saída.
Use-o por uma janela curta e controlada:
redis-cli MONITOR | head -n 200
Depois pare. Em produção, prefira amostragem de logs de aplicação, estatísticas de comandos Redis ou uma janela de manutenção curta quando possível.
INFO commandstats é frequentemente mais seguro para uma visão ampla:
redis-cli INFO commandstats
Mostra contagens de chamadas por comando e microssegundos cumulativos. Não dirá qual chave estava lenta, mas pode revelar que uma aplicação está emitindo muito mais chamadas HGETALL, KEYS ou EVAL do que o esperado.
Scripts Lua Precisam de Limites
Scripts Lua são poderosos porque executam atomicamente dentro do Redis. Esse mesmo comportamento atômico significa que um script longo bloqueia outros comandos enquanto executa. Scripts lentos frequentemente vêm de loops sobre grandes coleções, descoberta ilimitada de chaves ou lógica que cresceu de um pequeno ajudante para uma mini aplicação.
Revise scripts com as mesmas perguntas:
- Quantas chaves isso pode tocar?
- Quantos elementos isso pode percorrer?
- O que acontece quando a chave de entrada tem um milhão de membros?
- O trabalho pode ser dividido em partes menores?
- O script retorna um payload grande?
Se um script aparecer no SLOWLOG, resista à tentação de apenas aumentar o slowlog-log-slower-than. O log está dizendo que um bloco atômico está demorando o suficiente para afetar outros clientes.
Persistência, Forks e "Comandos Lentos" Que São Sintomas
Às vezes os comandos são lentos porque o Redis está ocupado com trabalho em segundo plano. Snapshots RDB e operações de reescrita AOF podem aumentar CPU, pressão de memória e I/O de disco. No Linux, bifurcar um processo Redis grande também pode criar picos de latência, especialmente quando overcommit de memória, páginas enormes ou armazenamento lento estão envolvidos.
Verifique:
redis-cli INFO persistence
redis-cli INFO stats
redis-cli INFO memory
redis-cli LATENCY LATEST
Se os picos de latência coincidirem com salvamentos em segundo plano ou reescritas AOF, ajuste a persistência cuidadosamente. Você pode precisar de armazenamento mais rápido, políticas de salvamento ajustadas, limites de reescrita AOF ou configurações de memória. Não desabilite a persistência apenas para fazer um benchmark parecer melhor, a menos que o Redis seja puramente um cache descartável e o negócio aceite perder os dados.
Comportamento do Cliente Pode Sobrecarregar o Redis Sem Um Comando Ruim
Um servidor Redis pode ser prejudicado por milhões de pequenas chamadas ineficientes tanto quanto por um comando obviamente lento. Uma página que faz 200 chamadas GET sequenciais parecerá lenta mesmo que cada GET individual seja rápido.
Use pipelining quando a aplicação precisar de muitos comandos independentes e puder tolerar receber as respostas juntas:
GET user:1
GET user:2
GET user:3
enviado como um pipeline evita uma ida e volta por comando. Pipelining não substitui uma boa modelagem de dados e pode aumentar o uso de memória se os lotes forem muito grandes. Comece com tamanhos de lote modestos e meça.
Também inspecione os pools de conexão. Se os logs da aplicação mostrarem chamadas Redis levando 500 ms mas o Redis não vê comandos lentos, a aplicação pode estar esperando por uma conexão livre. Aumente o pool apenas depois de verificar por que as conexões existentes estão ocupadas. Um pool maior pode esconder o sintoma enquanto aumenta a pressão sobre o Redis.
Um Checklist Prático de Incidentes
Quando a latência do Redis está prejudicando os usuários, colete fatos nesta ordem:
date -u
redis-cli PING
redis-cli --latency -i 1
redis-cli SLOWLOG GET 20
redis-cli INFO commandstats
redis-cli INFO clients
redis-cli INFO memory
redis-cli INFO persistence
redis-cli LATENCY LATEST
Depois pergunte o que mudou: um deploy, um novo endpoint, um evento de crescimento de dados, um job em lote, uma migração, uma consulta de dashboard, um novo padrão de chave de cache ou uma reescrita de persistência. Lentidões no Redis são frequentemente ligadas a um único padrão de acesso que se tornou popular ou a uma chave que se tornou muito maior do que o esperado.
Para cada comando lento, anote o padrão de chave e o proprietário. "SMEMBERS lento" não é suficiente. "O serviço de recomendações chama SMEMBERS product:123:viewers em um conjunto que pode crescer sem limite" é acionável.
Resumo do Checklist de Ajuste de Desempenho
- Ative e Monitore o
SLOWLOG: Revise periodicamenteSLOWLOG GETpara identificar comandos lentos recorrentes. Ajusteslowlog-log-slower-thanse necessário. - Use
MONITORcom Cautela: Para depuração em tempo real durante suspeitas de lentidão, mas desabilite imediatamente depois. - Evite
KEYSem grandes espaços de chaves de produção: UseSCANpara iteração incremental quando a descoberta de chaves for genuinamente necessária. - Otimize Scripts Lua: Garanta que scripts
EVALeEVALSHAsejam eficientes e não executem excessivamente longos. - Processe Estruturas de Dados Grandes Iterativamente: Use
SSCAN,ZSCAN,LRANGEcom limites ouSCANem vez de buscar coleções inteiras. - Analise Argumentos de Comandos: Garanta que os argumentos passados aos comandos não estejam causando comportamento inesperado (ex.: contagens muito grandes, padrões complexos).
- Monitore Recursos do Servidor: Fique de olho no uso de CPU, memória e rede do servidor Redis. Comandos lentos podem às vezes ser um sintoma de um servidor sobrecarregado.
- Otimizações do Lado do Cliente: Verifique se sua aplicação não está enviando comandos muito rapidamente ou em lotes ineficientes. Considere pipelining para múltiplos comandos quando apropriado.
Verificação Final
Use SLOWLOG para encontrar comandos que são lentos dentro do Redis, ferramentas de latência para capturar picos no lado do servidor e temporização da aplicação para capturar espera do cliente. Depois corrija o padrão de acesso, não apenas o limite. Comandos limitados, respostas menores, lotes sensatos e propriedade clara de chaves grandes fazem mais pelo desempenho do Redis do que perseguir mudanças de ajuste pontuais.