Conectar a Redis
Cómo conectarse a Redis y realizar operaciones básicas en Python, JavaScript y Java.
Visión General
Redis es un almacén de estructuras de datos en memoria usado para caching, gestión de sesiones, análisis en tiempo real y brokers de mensajes. Conectarse a Redis y usar sus tipos de datos principales de forma eficiente es una habilidad backend fundamental. Lo siguiente cubre operaciones básicas en Python, JavaScript y Java.
Cuándo Usar
- For alternatives, see Connect to MySQL.
Usa este recurso cuando:
- Implementas capas de cache para reducir la carga de la base de datos
- Construyes almacenes de sesiones para aplicaciones web stateless
- Creas leaderboards en tiempo real, rate limiters o colas de mensajes
Solución
Python
import redis
r = redis.Redis(
host='localhost',
port=6379,
db=0,
decode_responses=True
)
# Strings
r.set('user:1:name', 'Alice', ex=3600)
name = r.get('user:1:name')
# Lists
r.lpush('queue:tasks', 'task_1')
task = r.brpop('queue:tasks', timeout=5)
# Hashes
r.hset('user:1', mapping={'email': 'alice@example.com', 'role': 'admin'})
user = r.hgetall('user:1')
JavaScript
const Redis = require('ioredis');
const redis = new Redis({
host: 'localhost',
port: 6379,
db: 0
});
// Strings
await redis.set('user:1:name', 'Alice', 'EX', 3600);
const name = await redis.get('user:1:name');
// Lists
await redis.lpush('queue:tasks', 'task_1');
const task = await redis.brpop('queue:tasks', 5);
// Hashes
await redis.hset('user:1', 'email', 'alice@example.com', 'role', 'admin');
const user = await redis.hgetall('user:1');
Java
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class RedisExample {
private final JedisPool pool = new JedisPool("localhost", 6379);
public void stringOps() {
try (Jedis jedis = pool.getResource()) {
jedis.setex("user:1:name", 3600, "Alice");
String name = jedis.get("user:1:name");
}
}
public void listOps() {
try (Jedis jedis = pool.getResource()) {
jedis.lpush("queue:tasks", "task_1");
var task = jedis.brpop(5, "queue:tasks");
}
}
public void hashOps() {
try (Jedis jedis = pool.getResource()) {
jedis.hset("user:1", "email", "alice@example.com");
jedis.hset("user:1", "role", "admin");
Map<String, String> user = jedis.hgetAll("user:1");
}
}
}
Explicación
Redis almacena datos en memoria, haciendo lecturas y escrituras extremadamente rápidas (sub-milisegundo). Los Strings son el tipo más simple, frecuentemente usados para cachear objetos serializados. Las Lists implementan colas (LPUSH + BRPOP para consumo bloqueante). Los Hashes almacenan objetos con múltiples campos de forma compacta. Los tres ejemplos usan una gestión de conexiones sólida: Python redis.Redis maneja reconexiones, JavaScript ioredis soporta clustering y pub/sub, y Java JedisPool reutiliza conexiones vía pooling.
Variantes
| Tecnología | Enfoque | Notas |
|---|---|---|
| Python | redis-py | Cliente oficial, soporta async vía aredis |
| JavaScript | node-redis | Alternativa a ioredis, soporte nativo de Promise |
| Java | Lettuce | Cliente Redis reactivo y async para Spring |
Lo que funciona
- Usa pooling de conexiones en todos los lenguajes para evitar agotamiento de conexiones
- Establece expiración explícita (
EX,PX,EXAT) en claves de cache para prevenir crecimiento descontrolado de memoria - Usa
decode_responses=Trueen Python para obtener strings en lugar de bytes - Prefiere
BRPOP/BLPOPpara consumo de colas para evitar busy-waiting - Usa pipelines de Redis (
.pipeline()/.multi()) para batch de múltiples comandos y reducir RTT
Errores Comunes
- Almacenar objetos grandes (>1 MB) en claves individuales, causando picos de latencia
- No manejar fallos de conexión, causando errores en cascada cuando Redis no está disponible
- Usar Redis como base de datos primaria en lugar de cache o almacén transitorio
- Olvidar establecer TTLs, provocando agotamiento de memoria y kills por OOM
- Usar el comando
KEYSen producción, que bloquea toda la instancia de Redis
Buenas prácticas adicionales
- Usa
SCANen lugar deKEYSen producción.KEYSbloquea toda la instancia de Redis.SCANitera en lotes pequeños:
for key in r.scan_iter(match="user:*", count=100):
print(key)
- Configura
maxmemoryymaxmemory-policy. Configura Redis para evictar claves cuando la memoria está llena:
# redis.conf
maxmemory 2gb
maxmemory-policy allkeys-lru
- Usa
EXPIREpara claves de sesión. Siempre establece un TTL en datos de sesión para prevenir crecimiento descontrolado:
r.setex('session:abc123', 3600, json.dumps(session_data))
- Monitorea estadísticas con
INFO. Rastrea uso de memoria, clientes conectados y estadísticas de comandos:
info = r.info()
print(f"Memoria usada: {info['used_memory_human']}")
print(f"Clientes conectados: {info['connected_clients']}")
print(f"Hits keyspace: {info['keyspace_hits']}")
print(f"Misses keyspace: {info['keyspace_misses']}")
- Usa
SENTINELpara alta disponibilidad. Redis Sentinel provee failover automático cuando un master cae. Usa clientes conscientes de sentinel en todos los lenguajes.
Tips de Rendimiento
-
Usa
PIPELINEpara operaciones masivas. Reduce round-trips de red enviando múltiples comandos en un solo batch. -
Usa
MGET/MSETpara operaciones de múltiples claves. Más rápido que llamadasGET/SETindividuales:
values = r.mget('key1', 'key2', 'key3')
r.mset({'key1': 'val1', 'key2': 'val2'})
- Usa
HGETALLcon moderación en hashes grandes. Si un hash tiene muchos campos, usaHSCANpara iterar:
for field, value in r.hscan_iter('large_hash', count=100):
print(field, value)
- Habilita
lazyfree-lazy-evictionen Redis 4.0+. El borrado async previene picos de latencia durante la evicción de claves:
# redis.conf
lazyfree-lazy-eviction yes
lazyfree-lazy-expire yes
lazyfree-lazy-server-del yes
- Usa
OBJECT ENCODINGpara inspeccionar el almacenamiento. Redis usa encodings compactos para estructuras de datos pequeñas. Verifica si tus claves usan encodings eficientes:
encoding = r.object('encoding', 'mykey')
print(f"Encoding: {encoding}") # ej. ziplist, hashtable, int Preguntas frecuentes
¿Debería usar Redis como mi base de datos principal?
No. Redis es un almacén en memoria mejor adaptado para caching, sesiones, colas y datos en tiempo real. Úsalo junto a una base de datos persistente como PostgreSQL o MySQL.
¿Cómo manejo el modo clúster de Redis?
Usa clientes conscientes de clúster: redis-py-cluster (Python), ioredis con new Redis.Cluster() (JS), o JedisCluster / Lettuce (Java).
¿Cuál es la diferencia entre EX y PX en Redis?
EX establece expiración en segundos. PX establece expiración en milisegundos. Ambos logran el mismo objetivo; usa el que se ajuste a tus requerimientos de precisión.
Python con pipelines y transacciones
import redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
# Pipeline: batch de múltiples comandos para reducir RTT
pipe = r.pipeline()
pipe.set('user:1:name', 'Alice', ex=3600)
pipe.set('user:1:email', 'alice@example.com', ex=3600)
pipe.hset('user:1', mapping={'name': 'Alice', 'email': 'alice@example.com'})
results = pipe.execute()
print(results) # [True, True, 1]
# Transacción con MULTI/EXEC
pipe = r.pipeline(transaction=True)
pipe.multi()
pipe.set('counter', 0)
pipe.incr('counter')
pipe.incr('counter')
results = pipe.execute()
print(results) # [True, 1, 2]
JavaScript con sorted sets y pub/sub
const Redis = require('ioredis');
const redis = new Redis({ host: 'localhost', port: 6379 });
// Sorted sets: leaderboards
await redis.zadd('leaderboard', 100, 'alice', 85, 'bob', 120, 'charlie');
const topPlayers = await redis.zrevrange('leaderboard', 0, 9, 'WITHSCORES');
// ['charlie', '120', 'alice', '100', 'bob', '85']
// Pub/Sub
const subscriber = new Redis({ host: 'localhost', port: 6379 });
const publisher = new Redis({ host: 'localhost', port: 6379 });
await subscriber.subscribe('notifications');
subscriber.on('message', (channel, message) => {
console.log(`Recibido en ${channel}: ${message}`);
});
await publisher.publish('notifications', JSON.stringify({ event: 'user_joined', userId: 42 }));
Java con JedisPool y scripting Lua
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.List;
public class RedisAdvanced {
private final JedisPool pool;
public RedisAdvanced(String host, int port) {
this.pool = new JedisPool(host, port);
}
public void rateLimit(String userId, int maxRequests, int windowSeconds) {
try (Jedis jedis = pool.getResource()) {
String key = "rate:" + userId;
String luaScript =
"local current = redis.call('INCR', KEYS[1]) " +
"if current == 1 then " +
" redis.call('EXPIRE', KEYS[1], ARGV[2]) " +
"end " +
"return tonumber(current) <= tonumber(ARGV[1])";
Object allowed = jedis.eval(
luaScript, 1, key,
String.valueOf(maxRequests), String.valueOf(windowSeconds)
);
System.out.println("Request permitido: " + allowed);
}
}
public void pipelineExample() {
try (Jedis jedis = pool.getResource()) {
var pipe = jedis.pipelined();
pipe.set("key1", "value1");
pipe.set("key2", "value2");
pipe.sync();
}
}
}
Python async con redis-py
import asyncio
import redis.asyncio as aioredis
async def main():
r = aioredis.Redis(host='localhost', port=6379, decode_responses=True)
# Operaciones async con strings
await r.set('async:key', 'value', ex=60)
value = await r.get('async:key')
# Pipeline async
pipe = r.pipeline()
pipe.set('a', 1)
pipe.set('b', 2)
pipe.get('a')
results = await pipe.execute()
print(results) # [True, True, b'1']
await r.close()
asyncio.run(main())
Recursos Relacionados
Conectar a MySQL
Cómo conectar a bases de datos MySQL en Python, JavaScript y Java.
RecipeConectar a PostgreSQL
Cómo conectar a bases de datos PostgreSQL en Python, JavaScript y Java.
PatternPatrón Cache-Aside
Carga datos en el caché bajo demanda desde el almacenamiento principal. Un patrón de caché que da a la aplicación control total sobre qué y cuándo cachear.
RecipePatrones de Cache de Redis para Aplicaciones de Alto
Como implementar patrones cache-aside, write-through y write-behind con Redis para reducir carga de base de datos y mejorar tiempos de respuesta
PatternPatrón Abstract Factory
Crea familias de objetos relacionados sin especificar sus clases concretas. Patrón de diseño creacional para familias de objetos consistentes.
RecipeEjecutar SQL Crudo
Cómo ejecutar consultas SQL crudas de forma segura con sentencias parametrizadas.