En el desarrollo de portales web modernos —especialmente aquellos con una alta carga visual e información estática como portales de turismo o catálogos— la velocidad de carga es crucial para la experiencia del usuario (UX) y el posicionamiento SEO. Consultar repetidamente una base de datos para obtener información que apenas varía ralentiza la aplicación y eleva innecesariamente los costes en la nube.

Para entenderlo de forma sencilla: imagina que trabajas en una biblioteca. Cada vez que un usuario te pide un mapa de Madrid, tienes que levantarte, ir al almacén del fondo, buscarlo y traérselo (esto sería una consulta tradicional a la base de datos). ¿No sería más eficiente dejar una copia del mapa en tu mostrador tras la primera búsqueda para entregarla instantáneamente a los siguientes usuarios? Eso es, exactamente, la memoria caché.

En este artículo técnico implementaremos paso a paso el patrón Cache-Aside utilizando una capa de Caché Distribuida con Redis, un backend en ASP.NET Core (Web API) y una interfaz moderna en React.

El Problema: Latencia en Consultas Repetitivas

Imagina un portal de turismo como Madrid Discovery. Cada vez que un usuario accede al sitio para ver los puntos de interés (el Museo del Prado, el Parque del Retiro, etc.), el frontend solicita los datos al backend.

En un entorno real con miles de usuarios concurrentes, esto provoca:

  • Las llamadas continuas a la base de datos ralentizan el servidor.
  • El tiempo de procesamiento (latencia) desmejora la experiencia del usuario.
  • Se desperdician recursos de computación procesando la misma consulta una y otra vez.

Para simular este escenario del «mundo real» en nuestro entorno de desarrollo, hemos inducido un retardo artificial de 1.5 segundos en nuestro repositorio de datos.

La Solución: Caché Distribuida (Redis vs Memoria RAM)

Para solucionar esto de manera eficiente, aplicamos el patrón Cache-Aside:

  • Cuando entra una petición, comprobamos si los datos ya están en nuestra memoria rápida (Redis).
  • HIT (Acierto): Si están, los devolvemos instantáneamente.
  • MISS (Fallo): Si no están, los consultamos a la base de datos lenta, los guardamos en Redis para el futuro y se los enviamos al usuario.

¿Por qué Redis y no la memoria local de .NET?

Si nuestro servidor backend necesita escalarse en la nube (creando múltiples instancias de la API), la caché local se quedaría aislada en cada servidor. Al usar Redis, todas las instancias comparten una única caché centralizada y externa.

Tutorial Paso a Paso

A continuación, detallamos la configuración completa del entorno y el backend.

Paso 1: Levantar Redis con Docker

Para no complicar la instalación en tu máquina local, utilizaremos un contenedor oficial de Redis. Asegúrate de tener Docker de escritorio activo y ejecuta el siguiente comando en tu terminal para levantar el servicio en el puerto estándar 6379:

Paso 2: Instalar el paquete de NuGet en .NET

En tu proyecto Web API de ASP.NET Core, instala el cliente oficial de caché distribuida para Redis ejecutando:

(Este paquete nos permitirá comunicarnos de forma nativa con el contenedor que acabamos de levantar).

Paso 3: Configurar la Cadena de Conexión

Abre tu archivo de configuración appsettings.json y añade la dirección de tu Redis local:

Paso 4: Registrar el Servicio en Program.cs

Configuramos la inyección de dependencias en Program.cs. Añadiremos una lógica de fallback inteligente: si la cadena de conexión de Redis existe, la API se conectará al contenedor; si no, utilizará la memoria local de .NET de manera temporal para facilitar el desarrollo.

Paso 5: Implementar la Lógica en el Controlador (.NET)

Inyectamos IDistributedCache en nuestro PlacesController. Como Redis trabaja únicamente con arreglos de bytes (byte[]), realizamos la serialización y deserialización en formato JSON (codificado en UTF-8) para guardar estructuras complejas.

Además, devolvemos propiedades de telemetría (ExecutionTimeMs y DataSource) para que el frontend pueda pintar en pantalla el origen exacto y la velocidad de los datos:

Paso 6: Consumir la API y Mostrar las Métricas en React

Para que nuestra interfaz muestre el Monitor de Rendimiento con los milisegundos reales, consumimos el endpoint de .NET guardando los metadatos del JSON en el estado de React.

Aquí tienes un ejemplo de cómo estructurar la petición en tu componente:

Resultados y Demostración en Vivo

Al interactuar con el frontend de React, el Monitor de Rendimiento refleja el cambio drástico de velocidad al instante:

  • Carga en Frío (Fallo de Caché / MISS): La primera vez que entramos o tras reiniciar la caché, el servidor tarda 1508 ms debido a la simulación de lectura en disco de la Base de Datos.
  • Carga en Caliente (Acierto de Caché / HIT): Al recargar o pulsar de nuevo el botón, los datos ya están en la RAM de Redis. El tiempo cae a 2 ms. Una gran reducción de velocidad del 99.8%

Prueba de Carga y Estrés con Autocannon

Para comprobar el comportamiento del servidor bajo condiciones extremas (como cientos de usuarios concurrentes ingresando a la web a la vez), realizamos un benchmark utilizando Autocannon (herramienta de benchmarking en Node.js de alto rendimiento).

Lanzamos la prueba simulando 50 usuarios concurrentes durante 5 segundos:

Resultados de la Comparativa

MétricaBase de Datos (Caché Vacía / MISS)Redis Cache (RAM / HIT)
Peticiones por Segundo (Avg)33 req/sec7,500+ req/sec
Latencia Media (Avg)1,510 ms6.2 ms
Transferencia de Datos~24 KB/sec~5.4 MB/sec
  • Sin Caché: El hilo del servidor se bloquea esperando la respuesta de la base de datos para cada usuario. La API colapsa procesando apenas 33 peticiones por segundo.
  • Con Caché: Al servir directamente desde la memoria RAM de Redis, la API no sufre y procesa más de 7,000 peticiones por segundo sin despeinarse.

Obstáculos Superados y Recomendaciones

Serialización y tipos de datos complejos

IDistributedCache trabaja únicamente con arreglos de bytes (byte[]). Inicialmente, intentar guardar objetos complejos de forma directa produce errores de compilación. La solución óptima es transformar el objeto a string (JSON) con System.Text.Json y después convertirlo a binario usando Encoding.UTF8.GetBytes

Evitar Datos Obsoletos (Stale Data)

Si se añade un nuevo monumento en Madrid pero la caché sigue activa, los usuarios verán datos antiguos.

Recomendación: Utilizar tiempos de expiración absolutos razonables (de 2 a 5 minutos para contenido semiestático). Además, en los métodos de escritura (POST, PUT, DELETE) de tu API es obligatorio llamar explícitamente a _cache.RemoveAsync(cacheKey) para invalidar los datos antiguos en el mismo instante en que cambien.

Conclusiones e Interrogantes para Debate

La caché distribuida de Redis es una herramienta indispensable para garantizar la escalabilidad en sistemas Full Stack Cloud. Sin embargo, abre debates interesantes de diseño de arquitectura:

  • ¿Compensa siempre asumir la latencia de red de un servidor externo como Redis frente a la velocidad de la memoria RAM local (DistributedMemoryCache), considerando proyectos pequeños?
  • ¿Cómo diseñarías una estrategia de invalidación eficaz si cuentas con múltiples microservicios actualizando simultáneamente la misma base de datos distribuida?

Autor/a: Jorge Mori Felipe

Máster: Desarrollo Full Stack + Arquitecturas Cloud

Centro: Tajamar Tech

Año académico: 2025-2026

Código / recursos utilizados / Otros datos de interés: https://github.com/JorgeMori2/proyecto-tech-riders Almacenamiento en caché distribuido en ASP.NET Core | Microsoft Learn Distributed caching in ASP.NET Core | Microsoft Learn Docs

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.