Redis no es solo un almacén clave-valor: ¿qué estructuras de datos ofrece y qué problemas reales resuelve?
Hashes, sorted sets, listas y streams cargan cada uno sus propias garantías de complejidad y compromisos de durabilidad: elegir el correcto es la habilidad real.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
Redis no es solo un almacén clave-valor: ¿qué estructuras de datos ofrece y qué problemas reales resuelve?
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#Redis como servidor de estructuras de datos, no un mapa hash con puerto de red
El modelo mental de "Redis es solo un almacén clave-valor rápido" es el que más rápido se cae en una entrevista senior. El valor detrás de cualquier clave puede ser un string, pero con la misma facilidad puede ser un hash, un sorted set, una lista, un set, un stream, un bitmap o un HyperLogLog, cada uno con su propia familia de comandos y sus propias garantías de complejidad, y cada uno existe porque resuelve una clase específica de problema que un simple GET/SET no puede resolver sin traer todo el blob a tu aplicación y hacer el trabajo ahí.
#Hashes: un registro, no un blob
Un valor String te obliga a serializar un objeto completo para actualizar un solo campo. Un Hash guarda pares campo-valor bajo una sola clave, así que un campo individual se puede leer o escribir en O(1) sin tocar el resto.
HSET cart:user:42 sku:1001 2 sku:2002 1HGET cart:user:42 sku:1001HINCRBY cart:user:42 sku:1001 1HGETALL cart:user:42
Un carrito de compras, una sesión de usuario o un registro de producto encajan naturalmente en un hash: incrementar la cantidad de un SKU (HINCRBY) nunca requiere leer y reescribir todo el carrito como JSON. Compáralo con un valor de tipo string en JSON, donde la misma actualización significa GET, deserializar, mutar un campo, serializar, SET; y una carrera entre dos actualizaciones concurrentes puede descartar silenciosamente una de ellas.
#Sorted sets: el orden como característica de primera clase
Un Sorted Set (ZSET) guarda miembros únicos, cada uno con un score de punto flotante, mantenidos internamente en orden por score (una skip list más una tabla hash), así que las consultas por rango, ya sea por posición o por score, son baratas.
Tabla de posiciones (leaderboard), el uso canónico:
ZADD leaderboard:season1 4500 "player:17"ZADD leaderboard:season1 6200 "player:42"ZINCRBY leaderboard:season1 150 "player:17"ZREVRANGE leaderboard:season1 0 9 WITHSCORESZREVRANK leaderboard:season1 "player:17"
El rate limiting de ventana deslizante es el otro uso estrella, y vale la pena saberlo de memoria: usa la marca de tiempo actual como score, el id de la petición como miembro, recorta todo lo más viejo que la ventana, y luego cuenta lo que queda.
ZADD ratelimit:user:42 1735142400123 "req-a1b2"ZREMRANGEBYSCORE ratelimit:user:42 -inf (1735142400123-60000)ZCARD ratelimit:user:42
Si ZCARD retorna más del número de peticiones permitidas, rechaza la petición. Esto te da una ventana deslizante exacta (no la aproximación en cubos de un contador de ventana fija) en tres comandos, todos O(log N). ZADD, ZINCRBY, ZRANGE/ZREVRANGE y ZREMRANGEBYSCORE son todos O(log N) (u O(log N + M) para un rango de M resultados): suficientemente baratos como para correr en cada petición.
#Listas: una cola, hasta que deja de alcanzar
Una List es, por dentro, una lista doblemente enlazada: O(1) para insertar y sacar en cualquiera de los dos extremos, O(N) para acceder al medio. Eso la vuelve una cola de trabajos mínima y natural:
LPUSH jobs:emails '{"to":"a@x.com","template":"welcome"}'BRPOP jobs:emails 5
Esto funciona, y así es genuinamente como arrancan muchos sistemas de trabajos en segundo plano livianos. Su límite es la durabilidad: BRPOP elimina el elemento en el instante en que un worker lo recoge. Si ese worker se cae a mitad del trabajo, el trabajo simplemente desaparece: nunca hubo un paso de confirmación, porque una List no tiene el concepto de "en curso pero aún no confirmado como terminado".
#Streams: una lista que recuerda quién ya leyó qué
Un Stream es un log de solo-anexar (append-only), algo así como una partición liviana de Kafka viviendo dentro de Redis, donde las entradas nunca se eliminan al leerlas, y varios consumer groups pueden llevar cada uno su propia posición de forma independiente, con confirmación explícita.
XADD orders:events '*' orderId 9931 status "created"XREADGROUP GROUP shipping consumer-1 COUNT 10 STREAMS orders:events '>'XACK orders:events shipping 1735142400123-0XPENDING orders:events shipping
XADD anexa (el '*' le pide a Redis que genere automáticamente un id basado en timestamp); XREADGROUP le entrega a un consumer group las siguientes entradas no leídas; XACK marca una como terminada. Fundamentalmente, una entrada que fue entregada pero nunca confirmada permanece visible en la lista de entradas pendientes del grupo (XPENDING), así que los trabajos en vuelo de un worker caído no se pierden: simplemente esperan a que otro consumidor los reclame (XCLAIM). Esa es la garantía de durabilidad que una cola basada en List nunca tuvo.
#Eligiendo la estructura
| Estructura | Operaciones clave | Complejidad típica | Uso canónico |
|---|---|---|---|
| String | GET, SET, INCR | O(1) | Valores cacheados simples, contadores |
| Hash | HSET, HGET, HINCRBY | O(1) por campo | Objetos/registros (carrito, sesión) |
| Sorted Set | ZADD, ZRANGE, ZREMRANGEBYSCORE | O(log N) | Leaderboards, rate limiting de ventana deslizante, colas de prioridad |
| List | LPUSH, RPOP, BRPOP | O(1) en los extremos | Colas simples, listas de elementos recientes |
| Set | SADD, SINTER, SISMEMBER | O(1) agregar/verificar | Tags, membresía única, álgebra de conjuntos |
| Stream | XADD, XREADGROUP, XACK | O(1) amortizado por anexo | Logs de eventos, colas durables multi-consumidor |
🧠 Check your understanding
0/4 · 0/4 answered1. Necesitas un rate limiter de ventana deslizante exacto (no una aproximación de ventana fija). ¿Qué estructura y enfoque de Redis encaja mejor?
2. Un worker que usa BRPOP sobre una List de Redis se cae justo después de sacar un trabajo pero antes de terminarlo. ¿Qué pasa con ese trabajo?
3. ¿Qué le da un consumer group de un Stream que una cola basada en List simple no ofrece?
4. ¿Por qué es riesgoso ejecutar KEYS * contra un keyspace grande en producción?