Bancos chave-valor
O modelo mais simples e mais rápido, com o Redis e suas estruturas de dados como referência.
O problema: nem todo dado precisa de consultas
Muitos dados de um sistema são acessados sempre do mesmo jeito: pela chave. A sessão do usuário pelo identificador da sessão. O carrinho pelo usuário. O contador de requisições pelo cliente e pelo minuto. Para eles, as junções, os índices e as transações de um banco relacional são custo sem uso.
Um banco chave-valor guarda cada valor sob uma chave e o devolve pela mesma chave. O Redis, o mais usado, guarda tudo na memória e responde em frações de milissegundo. E vai além do chave-valor puro: o valor pode ser uma estrutura de dados com operações próprias.
O tópico de cache trata do Redis como cache. Este trata dele como banco: as estruturas, a atomicidade, o que trava o servidor e o que acontece com os dados numa queda. Os testes usaram o Redis 8.
As estruturas de dados
| Estrutura | O que guarda | Operações típicas | Usos |
|---|---|---|---|
| String | Um valor, inclusive números | GET, SET, INCR, SET ... NX EX | Sessões, contadores, travas simples |
| Hash | Campos e valores sob uma chave | HSET, HGET, HINCRBY | Um objeto: o carrinho, o perfil |
| List | Uma sequência | LPUSH, RPOP, LRANGE | Filas simples, últimos itens |
| Set | Valores únicos | SADD, SISMEMBER, SINTER | Marcadores, quem já viu, interseções |
| Sorted set | Valores únicos ordenados por uma pontuação | ZADD, ZINCRBY, ZRANGE, ZRANK | Placares, filas por prioridade, janelas de tempo |
| Stream | Um log de eventos com consumidores | XADD, XREADGROUP, XACK | Filas com confirmação, eventos |
| HyperLogLog | Uma contagem aproximada de distintos | PFADD, PFCOUNT | Visitantes únicos, com 12 KB por contador |
A escolha da estrutura é a modelagem do Redis. Um placar como string serializada obriga a ler, ordenar e gravar a lista inteira a cada ponto. Como sorted set, cada ponto é um ZINCRBY, e os dez primeiros saem de um ZRANGE, já ordenados.
Atômico é o comando
O Redis executa um comando de cada vez, numa única thread. Cada comando é atômico: nada acontece no meio dele. Mas dois comandos seguidos não são. Medimos com 10 clientes fazendo 200 incrementos cada, num contador:
| Forma | Valor final (o certo é 2.000) |
|---|---|
GET, somar 1 na aplicação, SET | 353 |
INCR | 2.000 |
Com GET e SET, 82% dos incrementos se perderam: vários clientes leram o mesmo valor e gravaram o mesmo resultado. É a atualização perdida, de novo. O Redis tem um comando atômico para quase todo caso comum (INCR, HINCRBY, ZINCRBY, SET ... NX). Para lógicas que precisam de vários passos, há os scripts em Lua e as funções, que rodam inteiros sem interrupção.
Idas e voltas custam mais que o comando
Cada comando enviado e esperado é uma ida e volta pela rede. O pipelining manda vários comandos de uma vez e lê as respostas no fim. Gravando 20 mil chaves:
| Forma | Comandos por segundo |
|---|---|
| Um comando por ida e volta | 13.148 |
| Pipeline | 96.343 |
Mais de sete vezes mais rápido, na mesma rede Docker. Com o Redis em outra máquina, a diferença cresce, porque a ida e volta pesa mais. Pipeline não é transação: os comandos de outros clientes podem se intercalar entre os seus.
Um comando lento para o servidor inteiro
A mesma thread única que garante a atomicidade faz de cada comando lento uma pausa para todos. Com 1 milhão de chaves, medimos a pior espera de um cliente que fazia GET simples enquanto outro listava as chaves:
| Comando | Duração | Pior GET de outro cliente |
|---|---|---|
KEYS sessao:* | 1.297 ms | 308 ms |
SCAN sessao:*, em lotes de 1.000 | 1.538 ms | 6,4 ms |
O KEYS percorre todas as chaves num comando só, e o servidor para durante esse tempo. O SCAN faz o mesmo trabalho em pedaços, devolvendo o controle entre eles. O mesmo vale para outras operações sobre estruturas grandes: HGETALL num hash de um milhão de campos, LRANGE 0 -1 numa lista enorme, DEL de uma chave gigante (UNLINK apaga em segundo plano).
Memória é o limite
O Redis guarda tudo na memória, e o maxmemory define o teto. Quando ele chega, a política de despejo (maxmemory-policy) decide o que acontece. Testamos gravando 100 mil valores de 500 bytes com o limite em 20 MB:
| Política | Gravações aceitas | Chaves no fim | A primeira chave gravada continua? |
|---|---|---|---|
noeviction | 34.592, e 65.408 recusadas com erro | 34.592 | Sim |
allkeys-lru | 100.000 | 34.411 | Não |
Com noeviction, o Redis recusa as gravações quando a memória acaba, com o erro command not allowed when used memory > 'maxmemory'. A aplicação fica sabendo. Com allkeys-lru, todas as gravações foram aceitas, e 65 mil chaves foram apagadas sem nenhum aviso para dar lugar às novas. Para um cache, é o comportamento desejado. Para um carrinho, uma sessão ou uma fila, é perda de dados silenciosa.
Persistência: o que sobra depois de uma queda
Por padrão, o Redis grava fotografias periódicas da memória em disco (RDB), por regras como "a cada 60 segundos, se houver 10 mil mudanças". Também pode gravar cada comando num log (AOF). Gravamos 10 mil chaves, confirmadas pelo servidor, e matamos o processo com kill -9 logo em seguida:
| Configuração | Chaves depois da queda | Gravações por segundo (redis-benchmark, 50 conexões) |
|---|---|---|
| Padrão (só fotografias) | 0 | — |
| Sem persistência | — | 117.371 |
AOF com appendfsync everysec | 10.000 | 115.607 |
AOF com appendfsync always | 10.000 | 21.349 |
Com o padrão, as 10 mil gravações confirmadas sumiram: nenhuma fotografia tinha sido tirada ainda. Com o AOF, todas sobreviveram. O kill -9 derruba o processo, mas não a máquina: o que o Redis já tinha escrito no arquivo continuava na memória do sistema operacional. Numa queda da máquina, que o teste não simulou, o everysec pode perder até cerca de um segundo de gravações, e o always não perde nenhuma, ao custo medido: cinco vezes menos gravações por segundo.
Por que isso importa quando a IA escreve o código
Um assistente que usa Redis costuma tratá-lo como um dicionário remoto:
GET, cálculo na aplicação eSET, em vez do comando atômico.- Um comando por ida e volta em laços, sem pipeline.
KEYSem código de produção para listar ou apagar chaves.- Estruturas inteiras serializadas em JSON numa string, em vez de hash, set ou sorted set.
- Dados que não podem se perder num Redis com a configuração padrão, ou com
allkeys-lru. - Chaves sem tempo de expiração, crescendo até a memória acabar.
O próximo nível compara os usos do Redis, as configurações de persistência e de memória, e as alternativas.
Prompts para aprofundar
Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.
Bancos chave-valor em cinco minutos
Explique em no máximo cinco minutos de leitura o que é um banco chave-valor como o Redis, para alguém que só usou bancos relacionais. Cubra só o essencial: o modelo chave-valor, as estruturas de dados do Redis, por que os comandos são atômicos, o que acontece quando a memória acaba e o que se perde numa queda. Use exemplos de sessões, contadores e placares. No final, me faça três perguntas curtas para eu verificar se entendi, e corrija as minhas respostas.
As estruturas de dados do Redis
Quero aprender a escolher a estrutura de dados certa no Redis. Aja como um mentor e, para cada estrutura (string, hash, list, set, sorted set, stream e HyperLogLog), uma de cada vez: 1. Explique o que ela guarda e as operações principais, com o custo de cada uma. 2. Me dê um problema e peça que eu escreva os comandos para resolvê-lo. 3. Corrija a minha resposta e mostre um erro comum com essa estrutura. Espere a minha resposta em cada etapa.
A thread única do Redis
Quero entender as consequências de o Redis executar os comandos numa única thread. Explique: 1. Por que isso torna cada comando atômico. 2. Por que dois comandos seguidos não são atômicos, com o exemplo de um contador. 3. Por que um comando lento para todos os clientes, com KEYS, HGETALL e DEL de chaves grandes. 4. Pipeline, MULTI/EXEC, WATCH e scripts em Lua: o que cada um garante. 5. Como encontrar comandos lentos em produção (SLOWLOG, LATENCY). Depois me mostre quatro trechos de código e pergunte qual deles tem um problema de atomicidade ou de bloqueio.
Persistência e memória no Redis
Quero entender o que acontece com os dados do Redis numa queda e quando a memória acaba. Explique: 1. Fotografias (RDB) e log de comandos (AOF), e as opções de appendfsync. 2. A diferença entre a queda do processo e a queda da máquina. 3. As políticas de maxmemory e quando cada uma faz sentido. 4. A replicação assíncrona e o que se perde quando o principal cai. 5. Por que separar o Redis de cache do Redis de dados. Depois me descreva três usos e peça que eu escolha a persistência e a política de memória de cada um.