# Caching en backend: qué es y cómo funciona

Descubre qué es el caching, dónde vive desde la memoria hasta el CDN y cómo implementar el patrón Cache-Aside para acelerar tus aplicaciones.

- URL: https://miguelramos.net/blog/caching-en-backend-que-es-y-como-funciona/
- Fecha: 2026-09-01
- Autor: Miguel Ramos
- Tags: Backend, Rendimiento, Ingeniería de software

---
El caching es el proceso de guardar una copia de un dato o respuesta que resulta costosa de obtener en un lugar más rápido de consultar, para evitar tener que calcularla o recuperarla desde cero cada vez. Cuando una aplicación responde mucho más rápido la segunda vez que solicitas el mismo recurso, una de las posibles razones es que esa respuesta ya se encontraba almacenada en una caché.

Esta técnica no solo reduce la latencia al evitar consultas repetitivas a una base de datos, disco u otro servicio, sino que también puede reducir considerablemente la carga sobre los sistemas que se encuentran detrás de la caché. Sin embargo, su aparente simplicidad esconde decisiones importantes: dónde almacenar los datos, cuánto tiempo conservarlos y cómo evitar que una copia desactualizada termine afectando al funcionamiento de la aplicación.

## Qué es un hit, un miss y la tasa de aciertos

Un acierto de caché (cache hit) ocurre cuando el dato solicitado ya se encuentra almacenado y puede devolverse sin consultar nuevamente la fuente principal. Por el contrario, un fallo (cache miss) ocurre cuando el dato no está disponible en la caché y la aplicación debe obtenerlo desde su fuente original, como una base de datos o una API externa.

En un escenario típico de Cache-Aside, después de obtener el dato durante un cache miss, la aplicación lo almacena en la caché para que las siguientes solicitudes puedan reutilizarlo.

Para medir la eficacia de una caché se utiliza el hit rate o tasa de aciertos. Si de cada 100 solicitudes 90 pueden resolverse desde la caché, el hit rate es del 90 %.

Un hit rate elevado puede reducir considerablemente el número de consultas que llegan a la fuente original, especialmente cuando existen muchas solicitudes repetitivas.

## Dónde vive la caché en una arquitectura moderna

La caché no tiene que encontrarse en un único lugar. Puede existir en diferentes capas de una arquitectura, dependiendo de qué información se quiere almacenar y de quién necesita acceder a ella.

Cada capa tiene sus propias características:

1. En memoria (In-Memory): La aplicación puede mantener datos directamente en la RAM de su propio proceso, por ejemplo mediante variables, mapas o diccionarios. Es una de las opciones con menor latencia, pero cada instancia mantiene su propia copia. Esto puede provocar diferencias entre servidores cuando la aplicación escala horizontalmente.

2. Caché distribuida: Servicios como Redis o Memcached permiten almacenar la información fuera del proceso de la aplicación. Varias instancias pueden consultar la misma caché, evitando mantener una copia independiente en cada servidor. El coste es añadir una comunicación de red adicional.

3. Navegador del cliente: Los navegadores pueden almacenar recursos y respuestas HTTP utilizando mecanismos definidos por HTTP, como "Cache-Control" y directivas como "max-age". Esto permite evitar solicitudes innecesarias al servidor cuando un recurso todavía puede reutilizarse.

4. Red de entrega de contenidos (CDN): Una CDN puede almacenar determinados recursos en servidores distribuidos geográficamente y responder desde ubicaciones cercanas al usuario. Esto reduce la distancia de red y también disminuye las solicitudes que llegan directamente al servidor de origen.

Estas capas incluso pueden combinarse. Una aplicación puede utilizar una caché en memoria para determinados datos, Redis para información compartida entre servidores y una CDN para archivos estáticos.

## El patrón Cache-Aside y su flujo de lectura

El patrón Cache-Aside, también conocido como lazy loading, es una de las estrategias más comunes para implementar cachés durante las operaciones de lectura. La aplicación consulta la caché primero y únicamente obtiene el dato desde la fuente original cuando todavía no existe una copia disponible.

El flujo básico consta de tres pasos:

* Se consulta la caché para comprobar si el dato existe.
* Si se encuentra (cache hit), se devuelve directamente.
* Si no se encuentra (cache miss), se consulta la fuente original, se obtiene el valor, se almacena en la caché y finalmente se devuelve al cliente.

De esta forma, el primer usuario que solicita un recurso puede asumir el coste de obtenerlo desde la fuente original, mientras que las siguientes solicitudes pueden reutilizar la copia almacenada.

Por ejemplo, imaginemos una API que obtiene el perfil de un usuario desde PostgreSQL. Sin caché, cada solicitud tendría que consultar la base de datos. Con Cache-Aside, el flujo podría ser:

```bash
Cliente
   │
   ▼
API
   │
   ▼
¿Existe en caché?
   │
   ├── Sí ──► Devolver dato
   │
   └── No
         │
         ▼
      Base de datos
         │
         ▼
      Guardar en caché
         │
         ▼
      Devolver dato
```

La ventaja principal es que las solicitudes repetitivas pueden resolverse sin volver a consultar la base de datos.

## El tiempo de vida y el problema de la invalidación

El Time to Live o TTL define cuánto tiempo puede permanecer una entrada en la caché antes de expirar automáticamente. El TTL ayuda a limitar cuánto tiempo puede permanecer almacenada una copia y, en estrategias basadas únicamente en expiración, también determina cuánto tiempo podríamos llegar a servir un dato desactualizado.

Por ejemplo, si almacenamos un producto con un TTL de 60 segundos, la aplicación podría reutilizar esa copia durante ese período. Cuando expire, una nueva solicitud tendría que obtener nuevamente el dato desde la fuente original.

Sin embargo, el TTL no resuelve todos los problemas de consistencia.

Si el producto cambia en la base de datos antes de que expire su entrada en la caché, la aplicación podría seguir devolviendo temporalmente la versión anterior.

Por eso, la invalidación de caché es uno de los problemas más importantes al diseñar este tipo de sistemas.

## Estrategias de escritura

Existen diferentes estrategias para coordinar las escrituras entre la caché y la fuente principal de datos:

* Write-through: La escritura pasa por la caché y se propaga hacia el almacenamiento principal como parte de la operación. Esto permite mantener ambas capas coordinadas, aunque añade trabajo y latencia a las escrituras.

* Write-back: El cambio se escribe inicialmente en la caché y posteriormente se persiste de forma asíncrona en el almacenamiento principal. Puede ofrecer escrituras rápidas, pero existe riesgo de pérdida de datos si la caché falla antes de que el cambio sea persistido.

* Invalidación por borrado: La aplicación actualiza primero la fuente principal y después elimina la entrada correspondiente de la caché. Cuando llegue la siguiente lectura, se producirá un cache miss, se recuperará el valor actualizado y se volverá a almacenar en la caché.

La estrategia adecuada depende de los requisitos de consistencia, latencia y tolerancia a fallos de cada aplicación.

## El problema de la Cache Stampede

Existe otro problema importante cuando una misma clave es muy solicitada.

Una cache stampede ocurre cuando una entrada muy popular expira y muchas solicitudes intentan reconstruirla al mismo tiempo. En lugar de que una sola solicitud consulte la base de datos y las demás esperen el resultado, todas pueden terminar realizando la misma consulta simultáneamente.

Por ejemplo:

```bash

             Caché
               │
        clave expirada
               │
     ┌─────────┼─────────┐
     ▼         ▼         ▼
   Req 1     Req 2     Req 3
     │         │         │
     └─────────┼─────────┘
               ▼
         Base de datos

```

Si cientos o miles de solicitudes hacen esto simultáneamente, la caché deja de proteger a la base de datos justo en el momento en que más se necesita.

Algunas técnicas para reducir este problema son:

* Single-flight o request coalescing: Permitir que una sola solicitud reconstruya el valor mientras las demás esperan su resultado.
* Jitter: Añadir una pequeña variación aleatoria a los TTL para evitar que muchas claves expiren exactamente al mismo tiempo.
* Refresh anticipado: Regenerar determinados valores antes de que lleguen a expirar.
* Stale-while-revalidate: Servir temporalmente un valor anterior mientras se genera una versión actualizada en segundo plano, cuando el caso de uso lo permite.

## Cuándo no deberías utilizar caché

Implementar una caché siempre añade cierta complejidad. Por eso, no tiene sentido introducirla simplemente porque una tecnología como Redis esté disponible.

Hay varios escenarios donde una caché puede no aportar suficiente valor o requiere especial cuidado:

* Datos que requieren frescura absoluta: Si una operación necesita consultar siempre el estado más reciente de la fuente de verdad, una caché potencialmente desactualizada puede ser inadecuada para esa operación.

* Datos con poca reutilización: Si prácticamente cada solicitud pide información diferente y las entradas nunca vuelven a consultarse, el hit rate será bajo y el coste de mantener la caché puede no compensar.

* Datos altamente personalizados: Que un dato sea personalizado no significa que no pueda cachearse, pero si cada respuesta es exclusiva de un usuario y tiene poca probabilidad de reutilización, la caché puede aportar poco beneficio.

* Aplicaciones pequeñas: Si la base de datos ya responde suficientemente rápido y el tráfico es bajo, introducir una infraestructura adicional puede aumentar la complejidad sin solucionar un problema real.

La pregunta correcta no es simplemente "¿puedo utilizar una caché?", sino:

«"¿Qué coste tiene obtener este dato, cuánto se reutiliza y qué nivel de desactualización puedo aceptar?"»

## Caché no significa consistencia garantizada

Es importante recordar que una caché normalmente contiene una copia de la información, no la fuente original de la verdad.

Cuando existe una base de datos como fuente principal y Redis como caché, pueden existir temporalmente dos versiones del mismo dato:
```bash
Base de datos
     │
     │ versión nueva
     ▼
   Redis
     │
     │ versión anterior
     ▼
   Cliente
```
Por eso, diseñar una estrategia de caché implica decidir qué nivel de consistencia necesita cada operación.

En algunas aplicaciones, unos segundos de desactualización son completamente aceptables. Por ejemplo, una página de estadísticas puede mostrar información ligeramente antigua sin afectar al funcionamiento del sistema.

En otras operaciones, como determinadas transacciones financieras o decisiones críticas de inventario, utilizar una copia potencialmente desactualizada podría producir resultados incorrectos. En esos casos, la fuente de verdad debe tener prioridad o debe utilizarse una estrategia de sincronización mucho más estricta.

## Conclusión

La caché es una herramienta para reducir latencia y carga, no una solución universal para hacer que una aplicación sea más rápida.

Su efectividad depende de varios factores: la frecuencia con la que se reutilizan los datos, el coste de obtenerlos, el hit rate, el TTL y la estrategia utilizada para mantener las copias actualizadas.

Patrones como Cache-Aside, herramientas como Redis y mecanismos como los TTL pueden mejorar considerablemente el rendimiento de un backend cuando se aplican sobre un problema real y con una estrategia de invalidación adecuada.

Como desarrollador y asesor tecnológico, en "miguelramos.net" (https://miguelramos.net) suelo insistir en que optimizar no significa añadir herramientas por añadirlas. Primero hay que identificar el cuello de botella y después elegir la estrategia adecuada.

Si necesitas optimizar la arquitectura de tus proyectos o identificar cuellos de botella en tu infraestructura, puedes "contactarme directamente desde aquí" (/#contacto) o escribirme por "WhatsApp" (https://wa.me/526634660998).

¿Te has enfrentado alguna vez a un fallo de caché difícil de depurar en producción?