Redis es el caché in-memory dominante en backends modernos. Pero “poner Redis delante” no es estrategia — es ingrediente. Los equipos que usan Redis eficientemente han internalizado patrones específicos de interacción con la base de datos, diseño de TTL, e invalidación. Este artículo repasa los más importantes.
Los cuatro patrones principales
Cache-aside (lazy loading)
El patrón más común. La aplicación consulta primero al caché; si falla, consulta la base de datos y guarda el resultado en caché.
def get_user(user_id):
cached = redis.get(f"user:{user_id}")
if cached:
return json.loads(cached)
user = db.query("SELECT * FROM users WHERE id = %s", user_id)
redis.setex(f"user:{user_id}", 3600, json.dumps(user)) # TTL 1h
return user
Pros: simple, resiliente si Redis cae (la app sigue funcionando con penalty). Contras: primer acceso siempre miss, riesgo de datos obsoletos.
Read-through
La aplicación consulta siempre al caché, y el caché se ocupa de rellenarse desde la base de datos cuando falta. Requiere biblioteca/proxy que medie.
Pros: lógica más simple en la aplicación. Contras: acoplamiento entre caché y base de datos, menos flexible.
Write-through
Al escribir, la aplicación actualiza simultáneamente caché y base de datos. Caché siempre refleja estado.
Pros: caché nunca está desactualizado. Contras: latencia de escritura aumenta (dos sistemas), si caché cae puede perderse si no hay write-back.
Write-behind (write-back)
La aplicación escribe solo al caché; un proceso asíncrono persiste a la base de datos después.
Pros: latencia de escritura mínima. Contras: riesgo de pérdida de datos si el caché cae antes del flush. Solo apropiado donde la durabilidad no es crítica.
Diseño de TTL
Decisión subestimada. TTL demasiado corto → muchos misses innecesarios. TTL demasiado largo → datos obsoletos. Reglas prácticas:
- Datos que cambian predeciblemente: TTL = tiempo entre cambios. Ej. tipo de cambio cada 5 min → TTL 5-10 min.
- Datos muy estables: TTL largo (horas o días). Perfil de usuario que cambia una vez cada varios meses.
- Datos críticos para precisión: TTL corto + invalidación explícita en los escritores.
- Precios, inventarios, datos que si desactualizados causan problemas: invalidación explícita, no solo TTL.
Un TTL bien elegido es uno de los mayores impactos en tasa de hit.
Thundering herd
Un problema clásico: muchas peticiones concurrentes llegan al mismo tiempo al miss del caché. Todas llaman a la base de datos simultáneamente, saturándola.
Soluciones:
- Lock en caché: el primer miss toma un lock, los demás esperan y leen del caché una vez rellenado. Patrón “request coalescing”.
- TTL con jitter: en lugar de TTL fijo, TTL = base + aleatorio entre 0 y N%. Evita que miles de claves expiren simultáneamente.
- Early refresh: antes de que el TTL expire, si la clave está por caducar, una sola petición refresca en background mientras el resto sirve del valor aún válido.
Librerías como ReadWriteRedisCache o cache-stampede implementan estos patrones.
Invalidación: lo difícil
“There are only two hard things in Computer Science: cache invalidation and naming things.” Phil Karlton lo dijo con razón.
Tres estrategias de invalidación:
TTL only
Deja que expire. Simple, puede ser suficiente para datos no críticos. No funciona cuando “ahora” importa.
Invalidación explícita en escritores
Cada código que escribe en la base de datos también invalida las entradas de caché relacionadas. Requiere disciplina y documentación.
def update_user(user_id, data):
db.update(...)
redis.delete(f"user:{user_id}")
redis.delete(f"user_list:active") # invalidar queries relacionadas
Event-based invalidation
Un log de cambios en la base de datos (CDC) dispara eventos que invalidan el caché. Más desacoplado pero añade complejidad operativa.
Patrones específicos de Redis
Aprovechar features nativos:
SCANen lugar deKEYSen producción.KEYSbloquea el servidor completo en bases grandes.- Pipelines para batch de operaciones. Un pipeline con 100 comandos reduce 100 RTTs a 1.
- Scripts Lua para operaciones atómicas complejas (ej. “incrementa este contador y devuelve el valor si < umbral”).
- Estructuras adecuadas: Sorted Set para leaderboards, HyperLogLog para cardinalidad aproximada, Stream para logs eventuales, GEO para geolocalización.
Cuándo no usar caché
Señal de que el caché no es la respuesta:
- Tasa de hit baja (<50%). El caché solo añade latencia y complejidad.
- Datos que cambian tan rápido que TTL sería <1s. El overhead del caché supera el valor.
- Datos únicos por petición (búsquedas complejas con parámetros muy variables). No hay oportunidad de reuso.
Ver nuestra cobertura relacionada sobre cuándo seguir con RabbitMQ para colas — patrones similares de decisión basada en caso de uso.
Conclusión
Redis es herramienta poderosa, pero no mágica. Los equipos que le sacan partido han invertido en diseño consciente de patrón (cache-aside vs write-through), TTL adecuado al dominio, invalidación bien orquestada, y defensa contra thundering herd. Sin esos fundamentos, “meter Redis” frecuentemente empeora sistemas en vez de mejorarlos.
Síguenos en jacar.es para más sobre arquitectura backend, rendimiento y bases de datos.