18 de julio de 2026
Redis: ¿por qué es tan rápido? (y cuándo no usarlo)
El problema que Redis resuelve
Antes de hablar de Redis hay que hablar del problema que lo hizo necesario. Una base de datos relacional bien indexada puede responder una query en unos pocos milisegundos. Eso suena rápido — hasta que tu endpoint necesita hacer esa consulta 50,000 veces por segundo, o hasta que esa consulta es literalmente la misma para los últimos 10,000 usuarios que visitaron tu home page.
El costo real no es la latencia de una sola query. Es la multiplicación: cada conexión a la base de datos consume un worker de tu pool de conexiones, cada disco tiene un límite físico de IOPS, y en algún punto tu base de datos relacional deja de ser el cuello de botella de tu lógica de negocio y se convierte en el cuello de botella de tu sistema completo — sin importar qué tan bien esté indexada.
Redis no reemplaza esa base de datos. Resuelve un problema distinto: qué hacer con los datos que se piden constantemente, que cambian poco, o que ni siquiera necesitan sobrevivir un reinicio del servidor. Sesiones de usuario, contadores, resultados de queries caras, el estado de quién está conectado ahora mismo — datos que, si vivieran en tu base de datos principal, estarían compitiendo por los mismos recursos que tus transacciones de negocio reales.
¿Por qué es tan rápido? El mecanismo real
La infografía dice "todo vive en RAM → sin lecturas a disco → microsegundos". Eso es cierto pero incompleto — y la parte que falta es la que de verdad explica por qué Redis es predecible bajo carga, no solo rápido en el caso feliz.
Acceder a RAM toma decenas de nanosegundos. Un SSD, decenas de microsegundos — mil veces
más lento. Un disco mecánico, milisegundos — un millón de veces más lento. Eso ya
explica gran parte de la velocidad. Pero lo que la mayoría de la gente no sabe es que
Redis es single-threaded para la ejecución de comandos: un solo hilo procesa todas
las operaciones, una a la vez, usando un event loop con multiplexado de I/O (similar al
de Node.js). Esto suena contraintuitivo — ¿cómo puede ser más rápido con un solo hilo? —
pero es exactamente lo que le da su consistencia: no hay locks, no hay race conditions
entre threads peleando por la misma estructura de datos, no hay context switching. Cada
comando corre hasta el final antes de que empiece el siguiente. Eso es lo que hace que un
INCR sea atómico sin que tengas que pensar en concurrencia — la atomicidad no viene de
magia, viene de que literalmente no hay nada más corriendo al mismo tiempo.
El trade-off de esa arquitectura: un comando lento (un KEYS * sobre una base de datos
de millones de llaves, o un script de Lua mal escrito) bloquea todo — no solo esa
operación, sino cada cliente conectado, porque el único hilo está ocupado. Es un error
común en producción: alguien corre un comando de mantenimiento en horario pico y toda la
aplicación se congela por unos segundos.
Los usos que de verdad importan
La infografía lista seis. Vale la pena entender el mecanismo detrás de cada uno, porque "usar Redis para X" no siempre significa lo mismo por dentro.
Caché. El patrón más común es cache-aside: tu aplicación primero pregunta a Redis,
si no está (cache miss) va a la base de datos, y escribe el resultado en Redis antes de
responder.
async function getUser(id) {
const cached = await redis.get(`user:${id}`)
if (cached) return JSON.parse(cached)
const user = await db.query('SELECT * FROM users WHERE id = $1', [id])
await redis.set(`user:${id}`, JSON.stringify(user), 'EX', 300)
return user
}
Simple en el papel. El problema real aparece cuando una llave con tráfico alto expira y cientos de requests concurrentes le pegan a la base de datos al mismo tiempo intentando regenerarla — eso se llama cache stampede o thundering herd, y es la razón por la que "solo cachéalo" no es una estrategia completa.
Sesiones. Funcionan porque son efímeras por naturaleza — si el usuario tiene que volver a iniciar sesión tras un reinicio del servidor, es molesto pero no catastrófico. Es exactamente el tipo de dato que NO debería competir por espacio en tu base transaccional.
Pub/Sub. Aquí es donde la gente se lleva la sorpresa más cara. PUBLISH/SUBSCRIBE
en Redis es fire-and-forget: si un suscriptor no está conectado en el momento exacto en
que se publica el mensaje, simplemente lo pierde. No hay cola, no hay reintento, no hay
persistencia. Es coordinación en tiempo real (notificaciones, "usuario X está escribiendo"),
no un sistema de mensajería confiable.
Colas. Si necesitas garantías de entrega, Redis Streams (XADD/XREADGROUP) es una
opción real — a diferencia de Pub/Sub, los streams sí persisten mensajes y soportan
consumer groups con acknowledgment. Pero sigue sin tener las garantías de un broker
dedicado como SQS o RabbitMQ en escenarios de alta criticidad (reintentos con backoff,
dead-letter queues nativas, particionado). Para colas ligeras internas, Streams es
suficiente. Para el flujo de pagos de tu negocio, no lo es.
Rate limiting. El patrón atómico real usa INCR + EXPIRE — pero hay un detalle que
casi nadie hace bien la primera vez (lo desarrollo abajo con código completo).
Leaderboards. Los Sorted Sets (ZADD, ZRANGE, ZREVRANGE) mantienen los elementos
ordenados por score con complejidad O(log N) tanto para insertar como para consultar por
rango — es la estructura de datos correcta para "dame el top 10" sin tener que ordenar
nada en tu código.
Buenas prácticas que casi nadie sigue hasta que les truena en producción
Usa TTL, siempre. Sin EXPIRE, una llave vive para siempre. Multiplica eso por miles
de sesiones de usuario que nadie limpia y tienes un OOM (out of memory) esperando a
pasar. La política de evicción (maxmemory-policy) importa tanto como el TTL mismo:
noeviction (el default) simplemente empieza a rechazar escrituras cuando se llena la
memoria — que en producción se ve como un 500 en cascada. allkeys-lru descarta las
llaves menos usadas recientemente, que casi siempre es lo que en verdad quieres para un
caché.
No lo caches todo. Cachear una consulta que ya es barata solo agrega complejidad (invalidación, otro punto de falla) sin beneficio real. Cachea lo que es caro de calcular Y se pide seguido — no una cosa o la otra.
Monitorea la memoria. INFO memory te da used_memory, mem_fragmentation_ratio, y
evicted_keys. Un mem_fragmentation_ratio mayor a 1.5 es señal de que el allocator de
memoria del sistema operativo está desperdiciando espacio — vale la pena revisarlo antes
de simplemente comprar más RAM.
Persistencia solo cuando la necesitas. RDB toma snapshots periódicos (rápido de recuperar, puedes perder los últimos minutos de datos). AOF registra cada escritura (mejor durabilidad, archivos más grandes, recuperación más lenta). Si Redis es solo tu caché, no actives ninguna — es una capa desechable por diseño y activar persistencia ahí es pagar el costo de I/O a disco por datos que, por definición, no te importa perder.
Dónde brilla de verdad
APIs de alto tráfico, aplicaciones en tiempo real, microservicios que necesitan estado compartido sin acoplarse a la base de datos de otro servicio, y — cada vez más relevante para lo que hago día a día — sistemas de IA: caché de embeddings ya calculados, resultados de prompts frecuentes, y rate limiting de llamadas a APIs de modelos que cobran por token. Redis también tiene un módulo de búsqueda vectorial (RediSearch) que lo pone en la misma conversación que Pinecone para ciertos casos — aunque para RAG en producción a escala, sigo prefiriendo una base vectorial dedicada.
Cuándo NO usar Redis
Ninguna infografía te va a decir esto, así que aquí va sin filtro:
- No es tu fuente de verdad. Si perder esos datos te causaría un problema real (inventario, saldos, pedidos), pertenecen a una base de datos con garantías ACID reales, no a un almacén optimizado para velocidad sobre durabilidad.
- No es gratis a escala. RAM cuesta órdenes de magnitud más que disco por GB. Un dataset que "cabe fácil" en Postgres puede ser una factura de infraestructura muy distinta en Redis si lo tratas como base de datos primaria en vez de caché.
- No es bueno para queries relacionales. Sin joins, sin queries ad-hoc complejas. Si tu acceso a datos es "dame todo donde X y Y y ordenado por Z", eso es trabajo para SQL, no para llaves y valores.
- El single-thread es una espada de dos filos. Un script de Lua mal escrito, o un comando O(N) sobre una colección enorme, bloquea a todos los clientes conectados al mismo tiempo. No hay "otro hilo" que siga atendiendo mientras uno se tarda.
Ejemplo práctico: un rate limiter completo
Esta es la versión ingenua que la mayoría escribe primero — y por qué está rota:
// ❌ Race condition: si el proceso muere entre estas dos líneas,
// la llave queda viva para siempre sin TTL.
const count = await redis.incr(`rate:${userId}`)
await redis.expire(`rate:${userId}`, 60)
La versión correcta solo pone el TTL la primera vez que se crea la llave — cuando
INCR regresa 1 significa que la llave se acaba de crear en esta ventana:
async function isAllowed(userId, limit = 100, windowSeconds = 60) {
const window = Math.floor(Date.now() / 1000 / windowSeconds)
const key = `rate:${userId}:${window}`
const count = await redis.incr(key)
if (count === 1) {
await redis.expire(key, windowSeconds)
}
return count <= limit
}
Esto es un fixed window rate limiter — simple, atómico, y suficiente para la mayoría de los casos. Tiene un defecto conocido (permite hasta 2x el límite en el borde entre dos ventanas), que se resuelve con un sliding window log o el algoritmo GCRA si de verdad lo necesitas — pero empezar con la versión simple y medir si el problema del borde te afecta en la práctica es mejor ingeniería que optimizar prematuramente para un caso que tal vez nunca ocurra.
Mi conclusión
Redis no es "una base de datos rápida" — es una herramienta con un trade-off muy específico (velocidad y simplicidad de acceso a cambio de durabilidad) que resulta correcto para una porción sorprendentemente grande de los problemas reales que resuelvo en producción: sesiones, rate limiting, caché, y coordinación ligera entre servicios. Lo que he visto salir mal en equipos que lo adoptan por primera vez casi nunca es Redis en sí — es tratarlo como si fuera Postgres con esteroides, sin pensar en qué pasa el día que se reinicia el proceso, o el día que alguien corre un comando lento en horario pico. Úsalo para lo que es rápido de perder, no para lo que no puedes perder.
Este artículo acompaña la infografía de Redis — parte de la serie Tecnolog-IA con Inteligenc-IA.
Dónde lo he usado
En producción
- StudioEngine.ai — la API web corre en , pero el trabajo pesado (llamadas a las APIs de y para generar, escalar e inpaint-ear imágenes con y ) se procesa en contenedores de — así el servidor web nunca se bloquea esperando una respuesta que puede tardar segundos o minutos. Redis es el canal de mensajes entre ambos: cuando el proceso en Fargate termina una llamada a Replicate/OpenAI, publica el evento; la API web lo recibe y lo reenvía al frontend para avisarle al usuario que su imagen ya está lista — sin que el cliente tenga que estar preguntando "¿ya terminó?" cada dos segundos. Es el mismo patrón de Pub/Sub explicado arriba: rápido, desacoplado, y perfecto para coordinación entre servicios que no necesitan garantía de entrega.

Infografía generada con ChatGPT usando un prompt personalizado. —



