Entendendo o Keyspace do Redis: Comandos de Exclusão e Inspeção

Desbloqueie o poder do gerenciamento de keyspace do Redis com este guia abrangente. Aprenda a inspecionar seus dados com segurança usando `SCAN` (e por que evitar `KEYS` em produção) e a excluir chaves de forma eficiente com `DEL` e o não bloqueante `UNLINK`. Entenda a natureza destrutiva de `FLUSHDB` e `FLUSHALL` e descubra as melhores práticas para manter uma instância Redis saudável e de alto desempenho.

Entendendo o Keyspace do Redis: Comandos de Exclusão e Inspeção

O keyspace do Redis é simplesmente o conjunto de chaves no banco de dados atualmente selecionado, mas a maneira como você inspeciona e exclui essas chaves pode decidir se uma limpeza é entediante ou se sua aplicação trava no meio do tráfego.

A maioria das equipes aprende isso da maneira mais difícil. Alguém precisa excluir chaves session:* de um cache de staging, executa KEYS session:*, vê a lista e tenta o mesmo hábito em produção. Em um banco de dados pequeno, isso parece ok. Em uma instância ocupada com milhões de chaves, o comando pode segurar o servidor tempo suficiente para que requisições não relacionadas fiquem na fila atrás dele. O Redis processa comandos muito rapidamente, mas um comando que percorre todo o keyspace ainda precisa fazer o trabalho.

Para operações do dia a dia, pense em duas etapas separadas: encontre as chaves com segurança, depois exclua-as com segurança. Não trate a inspeção de chaves como uma leitura inofensiva. Um comando de leitura ainda pode ser caro.

Comece com o banco de dados que você está realmente usando

Antes de excluir qualquer coisa, confirme o endpoint e o banco de dados lógico.

redis-cli -h redis.example.internal -p 6379 INFO keyspace

A saída se parece com isso:

# Keyspace
db0:keys=154233,expires=129900,avg_ttl=2851412
db2:keys=32,expires=32,avg_ttl=60000

Isso informa quais bancos de dados lógicos contêm chaves. Muitas implantações Redis usam apenas o banco de dados 0. O Redis Cluster suporta apenas o banco de dados 0, então FLUSHDB e FLUSHALL merecem cuidado extra lá porque o modelo mental usual de "banco de dados selecionado" não é útil da mesma forma.

Se sua aplicação usa bancos de dados numerados em um Redis independente, selecione o correto explicitamente:

redis-cli -n 2 DBSIZE

DBSIZE retorna o número de chaves no banco de dados selecionado. Ele não mostra nomes e não substitui uma passagem de inspeção, mas é uma boa verificação de sanidade antes e depois da limpeza.

Use KEYS apenas quando o keyspace for pequeno e descartável

KEYS pattern retorna todas as chaves que correspondem a um padrão no estilo glob:

KEYS user:*
KEYS cache:product:???
KEYS *

As regras de padrão são úteis: * corresponde a qualquer sequência, ? corresponde a um caractere e intervalos entre colchetes como [0-9] correspondem a um caractere do intervalo.

O problema não é a correção. O problema é que KEYS escaneia todo o keyspace em um único comando. Em um Redis de desenvolvimento local com algumas centenas de chaves, isso é conveniente. Em um cache compartilhado, pode adicionar latência para todos os outros clientes enquanto o Redis está ocupado produzindo o resultado. O comando é documentado como um comando de keyspace com complexidade linear, então não deve fazer parte de scripts normais de limpeza em produção.

Eu ainda uso KEYS em dois lugares:

  • Um Redis local descartável enquanto escrevo testes.
  • Um banco de dados de staging pequeno onde já verifiquei DBSIZE e sei que o comando não pode me surpreender.

Em todos os outros lugares, use SCAN.

Use SCAN para inspeção em produção

SCAN é baseado em cursor:

SCAN 0 MATCH user:* COUNT 100

O Redis retorna duas coisas: o próximo cursor e um lote de chaves. Comece no cursor 0. Continue escaneando com o cursor retornado. Pare quando o Redis retornar o cursor 0 novamente.

1) "24576"
2) 1) "user:100"
   2) "user:101"

COUNT é uma dica, não uma promessa. O Redis pode retornar mais chaves, menos chaves ou até mesmo nenhuma chave para uma determinada iteração. MATCH filtra o que é retornado, mas o Redis ainda avança pelo keyspace. Um padrão restrito é útil para reduzir o trabalho do lado do cliente, mas não torna mágicamente cada varredura gratuita.

Para trabalho em shell, prefira redis-cli --scan porque ele oculta o loop do cursor:

redis-cli --scan --pattern 'session:*'

Para contar chaves correspondentes sem imprimir todas elas:

redis-cli --scan --pattern 'session:*' | wc -l

Para inspecionar tipos antes de excluir:

redis-cli --scan --pattern 'session:*' | head
redis-cli TYPE session:abc123
redis-cli TTL session:abc123
redis-cli MEMORY USAGE session:abc123

TTL é especialmente útil para decisões de limpeza. Se um namespace de cache já tem expirações razoáveis, você pode não precisar de uma exclusão em massa. Deixar as chaves expirarem naturalmente é geralmente menos arriscado do que forçar uma grande exclusão durante o horário comercial.

Exclua chaves conhecidas com DEL

DEL remove uma ou mais chaves e retorna quantas existiam:

DEL session:abc123
DEL session:abc123 session:def456 session:ghi789

Para chaves pequenas, DEL geralmente é suficiente. Excluir uma chave de string ou um pequeno hash não é o caso assustador. O caso que dói é excluir um valor agregado grande, como uma lista com um grande número de elementos, um conjunto usado como índice ou um hash que cresceu muito além de seu propósito original. O Redis remove a chave do keyspace, mas liberar um valor grande pode custar tempo no caminho principal.

Se você está excluindo uma chave que sabe que é pequena, use DEL. Se você está excluindo muitas chaves ou não tem certeza do tamanho delas, use UNLINK.

Prefira UNLINK para exclusão em massa ou de grandes valores

UNLINK tem a mesma forma que DEL:

UNLINK session:abc123
UNLINK cache:old:1 cache:old:2 cache:old:3

A diferença importante é a recuperação de memória. UNLINK remove as chaves do keyspace imediatamente e depois libera a memória de forma assíncrona. Isso o torna um padrão mais seguro quando você está limpando chaves que podem conter valores grandes.

Isso não significa que UNLINK é mágico. Você ainda pode criar pressão se seu script descobrir e desvincular milhões de chaves o mais rápido possível. O Redis ainda precisa processar os comandos, as réplicas ainda precisam receber as alterações e a memória ainda precisa ser recuperada. Acione a limpeza em massa para que você possa observar a latência e a memória enquanto ela é executada.

Um loop de limpeza prático se parece com isso:

redis-cli --scan --pattern 'session:*' |
  xargs -r -L 100 redis-cli UNLINK

Isso exclui em lotes de 100 chaves. Ajuste o tamanho do lote para seu ambiente. Em uma janela de manutenção silenciosa, você pode usar lotes maiores. Em um cache ativo compartilhado pelo tráfego voltado ao usuário, lotes menores com uma pequena pausa entre os lotes podem ser mais gentis:

redis-cli --scan --pattern 'session:*' |
while read -r key; do
  redis-cli UNLINK "$key" >/dev/null
  sleep 0.005
done

Essa versão é mais lenta, mas é fácil de parar e fácil de entender.

Tenha cuidado com exclusões por padrão

O Redis intencionalmente não fornece DEL user:*. Você precisa combinar a varredura e a exclusão você mesmo. Esse atrito é útil porque exclusões por padrão são onde os acidentes acontecem.

Antes de excluir:

redis-cli --scan --pattern 'user:*' | head -50
redis-cli --scan --pattern 'user:*' | wc -l

Olhe para a primeira amostra. Conte o tamanho alvo. Se sua limpeza esperada era "alguns milhares de sessões abandonadas" e a contagem é "a maior parte do banco de dados", pare e corrija o padrão.

Use convenções de nomenclatura que tornam a limpeza entediante:

app:prod:session:<id>
app:prod:rate-limit:<user-id>
app:prod:cache:product:<id>

Isso é mais verboso do que session:<id>, mas permite que você direcione um namespace com precisão. No Redis Cluster, os nomes das chaves também podem incluir hash tags como cart:{user123}:items para posicionamento de slot. Esteja ciente dessas convenções antes de escrever padrões amplos.

FLUSHDB e FLUSHALL são botões de reset

FLUSHDB remove todas as chaves do banco de dados selecionado:

FLUSHDB
FLUSHDB ASYNC

FLUSHALL remove todas as chaves de todos os bancos de dados lógicos:

FLUSHALL
FLUSHALL ASYNC

O Redis moderno suporta modificadores ASYNC e SYNC para comandos de limpeza. ASYNC agenda a liberação em segundo plano, o que ajuda a evitar uma grande pausa síncrona de liberação de memória. Isso não torna a operação reversível. Uma vez que as chaves desaparecem do keyspace, sua aplicação as vê como desaparecidas.

Antes de usar qualquer um dos comandos, quero três verificações:

redis-cli ROLE
redis-cli INFO keyspace
redis-cli CONFIG GET dir

ROLE ajuda a confirmar se você está conectado a um primário ou a uma réplica. INFO keyspace mostra o que será afetado. CONFIG GET dir geralmente dá outra dica sobre em qual instância você está, porque os diretórios de dados tendem a incluir caminhos específicos do ambiente.

Para scripts de reset de desenvolvimento, seja explícito:

redis-cli -h 127.0.0.1 -p 6379 -n 0 FLUSHDB ASYNC

Evite scripts que executam redis-cli FLUSHALL com padrões. Os padrões mudam quando um script é executado em outro host, em outro contêiner ou a partir de um runner de CI com variáveis de ambiente diferentes.

Verifique após a exclusão

Após uma limpeza, verifique tanto a contagem quanto o comportamento da aplicação:

redis-cli --scan --pattern 'session:*' | wc -l
redis-cli INFO memory
redis-cli INFO stats | grep expired_keys

A memória pode não cair instantaneamente após UNLINK ou limpeza assíncrona porque a liberação acontece em segundo plano e os alocadores podem manter a memória reservada para reutilização. Isso não é automaticamente um vazamento. Observe used_memory, latência e se a contagem de chaves se move na direção que você espera.

Para alterações em produção, anote o comando e o padrão exatos antes de executá-lo. Uma limpeza segura do Redis não é apenas o comando certo. É o comando certo, contra a instância certa, com um padrão que você amostrou, durante uma janela onde você pode observar o resultado.

Um runbook de limpeza de produção mais seguro

Para uma limpeza real, gosto de transformar o comando em um pequeno runbook em vez de um one-liner digitado de memória. O runbook não precisa ser sofisticado. Ele deve responder a quatro perguntas: qual instância, qual padrão, quantas chaves e quão rápido.

Comece com verificações somente leitura:

redis-cli -h redis.example.internal -p 6379 ROLE
redis-cli -h redis.example.internal -p 6379 INFO keyspace
redis-cli -h redis.example.internal -p 6379 --scan --pattern 'app:prod:session:*' | head -20
redis-cli -h redis.example.internal -p 6379 --scan --pattern 'app:prod:session:*' | wc -l

Em seguida, inspecione algumas chaves representativas:

redis-cli TYPE app:prod:session:sample
redis-cli TTL app:prod:session:sample
redis-cli MEMORY USAGE app:prod:session:sample

Se a amostra mostrar chaves que devem expirar naturalmente em alguns minutos, espere em vez de excluir. Se as chaves não têm TTL e claramente pertencem a dados abandonados, prossiga com uma limpeza limitada. Mantenha um terminal aberto com latência ou memória:

redis-cli --latency
redis-cli INFO memory

Para Redis de produção compartilhado, prefiro um script que pode ser interrompido sem perder o controle da intenção:

redis-cli --scan --pattern 'app:prod:session:*' |
while read -r key; do
  redis-cli UNLINK "$key" >/dev/null
  sleep 0.002
done

Essa não é a versão mais rápida. É a versão que lhe dá a chance de notar se a latência se move, os clientes reclamam ou o padrão era mais amplo do que o esperado. Quando a instância está silenciosa e a contagem é modesta, o loteamento com xargs -L 100 é bom. O ponto é escolher o ritmo intencionalmente.

Mais um hábito ajuda: salve a contagem antes e depois no ticket de incidente ou nota de implantação. "Chaves de sessão excluídas" não é suficiente. "48.213 chaves correspondentes a app:prod:session:* excluídas de db0 no redis-cache-01 usando UNLINK, nenhum aumento de latência observado" é o tipo de nota que economiza tempo depois.