Caché local (en el proceso) vs. caché distribuida: ¿cuál es el verdadero tradeoff?
Ambas mantienen los datos cerca del lector, pero una vive y muere con un solo proceso mientras la otra vive y muere con la red — la elección cambia tu consistencia, latencia e historia de fallos.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
Caché local (en el proceso) vs. caché distribuida: ¿cuál es el verdadero tradeoff?
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.#Dos lugares distintos para guardar el mismo dato
"Cachéalo" suena a una sola decisión, pero en realidad obliga de inmediato a tomar una segunda: cachéalo, ¿pero dónde? Una caché local (en el proceso) vive en la memoria de una sola instancia en ejecución de tu aplicación — un simple hash map, o una librería como Caffeine (JVM), o un mapa LRU mantenido dentro de un proceso de Node. Una caché distribuida — Redis, Memcached — es un servicio aparte, accesible por red, compartido a la vez por todas las instancias de tu aplicación. Ambas hacen que las lecturas sean rápidas al evitar un viaje de ida y vuelta hasta el sistema de registro. El tradeoff está en todo lo que rodea a esa velocidad: consistencia, huella de memoria, superficie operativa, y qué pasa cuando una instancia no está de acuerdo con otra.
La imagen cuenta casi toda la historia por sí sola: en la topología local existen tres cachés y ninguna sabe de las otras. En la topología distribuida existe una sola caché y todas las instancias ven el mismo estado.
#Dónde gana la caché local
Nada le gana en velocidad pura a una búsqueda en un hash map dentro del propio proceso — no hay serialización, no hay salto de red, no hay un proceso aparte que coordinar; son nanosegundos en vez del costo, real aunque submilisegundo, de un viaje de red de ida y vuelta. Eso hace que la caché local sea la decisión correcta para datos que se leen constantemente, cambian poco, y toleran estar ligeramente desincronizados entre instancias durante una ventana corta: expresiones regulares compiladas, configuración ya parseada, snapshots de feature flags refrescados cada pocos segundos, una tabla de referencia pequeña (códigos de país, símbolos de moneda) que cambia quizás una vez por trimestre. Además no agrega infraestructura nueva — no hay un clúster que aprovisionar, parchear, ni por el que recibir una alerta a las 3 a.m.
#Dónde gana la caché distribuida
En el momento en que la corrección depende de que todas las instancias estén de acuerdo — datos de sesión, los contadores de peticiones de un rate limiter, un conjunto de deduplicación, un almacén de claves de idempotencia — una caché local no solo es más lenta, es activamente incorrecta: la Instancia A podría autorizar una petición que la Instancia B habría limitado, porque cada una solo mira su propio contador. La caché distribuida es también la única opción real cuando el conjunto de datos es demasiado grande para duplicarlo cómodamente en el heap de cada instancia, o cuando tu aplicación escala de una forma en la que "estado por instancia" ni siquiera tiene sentido — funciones serverless sin estado que arrancan en frío constantemente no obtienen prácticamente ningún beneficio de una caché local, porque no queda un proceso "caliente" que la retenga.
#El problema de invalidación que crea la caché local
Este es el tradeoff que agarra desprevenidos a los equipos en producción. Con una caché distribuida, invalidar un valor al escribir es una sola operación: borra la clave en Redis, y la siguiente lectura de cada instancia ve el cambio, porque siempre hubo una sola caché. Con caché local, esa misma escritura ahora tiene que llegar a la copia en memoria de cada instancia — lo que significa transmitir un mensaje de invalidación (vía Redis Pub/Sub, Kafka, o un bus similar) a todos los procesos, y aceptar el hecho de que, hasta que ese mensaje llegue, una parte de tu flota sigue sirviendo el valor viejo. Si ese broadcast falla en una instancia — un mensaje perdido durante un despliegue, una instancia nueva que se unió después de la escritura — obtienes un bug que parece de caché pero en realidad es un bug de consistencia distribuida disfrazado de bug de caché.
#La respuesta híbrida: caché L1/L2
Los sistemas de alto throughput con frecuencia usan ambas a la vez en vez de elegir una. Una caché local pequeña y de TTL corto (L1) se coloca delante de una caché distribuida más grande y de vida más larga (L2): la mayoría de las lecturas se sirven en nanosegundos desde L1, un fallo en L1 cae a L2 (igual mucho más barato que la base de datos), y solo un fallo verdadero en ambas llega al origen. A esto suele llamársele "near cache". El TTL corto de L1 acota qué tan desactualizada puede quedar una instancia — segundos, no minutos — lo que hace tolerable el problema de propagar invalidaciones: no necesitas un mecanismo de broadcast perfecto si el peor caso se autocorrige en un par de segundos de todas formas.
#Comparando ambas
| Dimensión | Local (en el proceso) | Distribuida |
|---|---|---|
| Latencia | La más rápida posible — sin salto de red | Rápida, pero un viaje de red real |
| Consistencia entre instancias | Ninguna por defecto — cada instancia tiene su propia vista | Una vista única y compartida |
| Costo de memoria | Duplicado en cada instancia | Almacenado una vez, compartido |
| Invalidación | Requiere transmitir a todas las instancias | Una operación, se siente en todas partes |
| Infraestructura nueva | Ninguna | Un clúster de caché que operar |
| Sobrevive a reinicios / cold starts | No — empieza vacía cada vez | Sí — se mantiene caliente sin importar el ciclo de vida de la app |
| Ideal para | Datos pequeños, de cambio lento, seguros por instancia | Cualquier cosa que requiera acuerdo entre instancias o un dataset grande |