La forma más eficiente de hacer un trabajo es no tener que hacerlo. Si mil usuarios piden la misma página de producto en una hora y el producto no ha cambiado, calcularla mil veces es un derroche. Una caché guarda el resultado la primera vez y lo reutiliza. Es una de las técnicas más potentes del backend, y también una de las más fáciles de hacer mal.
1 // Qué es una caché
Una caché es un almacén temporal de resultados que ya se han calculado o descargado, colocado más cerca de quien los pide o en un soporte más rápido que el original. Funciona siempre igual:
- Llega una petición.
- Se mira si el resultado está en la caché y sigue siendo válido.
- Si está (acierto o hit), se devuelve directamente.
- Si no está (fallo o miss), se calcula, se guarda en la caché y se devuelve.
Desde el punto de vista de la sostenibilidad, cada acierto es trabajo que no se repite: consultas que no se lanzan, plantillas que no se renderizan, bytes que no viajan. Pero la caché no es gratis: ocupa memoria, que consume energía todo el tiempo, y añade una pieza más al sistema.
2 // Las capas de caché
Una petición web puede encontrar una caché en varios puntos del camino. Cuanto antes se detiene, más trabajo se ahorra:
| Capa | Cómo se controla | Qué ahorra |
|---|---|---|
| Navegador | Cabeceras HTTP Cache-Control, ETag |
Todo: no hay ni petición o sólo el cuerpo, si responde 304 |
| CDN o proxy | Cabeceras HTTP y reglas del proveedor | El viaje hasta el servidor de origen y todo su trabajo |
| Aplicación | Redis, Memcached o memoria del proceso | Consultas, cálculos y llamadas a otros servicios |
| Base de datos | Memoria del propio motor | Lecturas de disco repetidas |
La capa del navegador merece un detalle. Con Cache-Control: max-age=31536000 le dices al navegador que puede reutilizar un recurso durante un año sin preguntar. Es lo adecuado para ficheros cuyo nombre cambia cuando cambia su contenido, como app.3f9a2c.js, que generan los empaquetadores. Para recursos que pueden cambiar, la cabecera ETag permite que el navegador pregunte «¿ha cambiado esto?» y el servidor responda 304 Not Modified sin volver a enviar el cuerpo.
Un error muy frecuente es servir todos los recursos estáticos sin cabeceras de caché, o con no-cache por defecto. Cada visita vuelve a descargar las mismas hojas de estilo, scripts y fuentes. Revisa la pestaña Red de las DevTools: si ves los mismos ficheros descargándose completos en cada recarga, hay trabajo gratis por hacer.
3 // Tiempo de vida e invalidación
Hay una frase célebre en informática, atribuida a Phil Karlton: sólo hay dos cosas difíciles, invalidar la caché y poner nombres a las cosas. El problema es que un dato guardado puede quedarse obsoleto cuando cambia el original. Hay dos estrategias básicas:
- Tiempo de vida (TTL): cada entrada caduca pasado un tiempo. Es sencillo y tolera que durante ese tiempo se sirva un dato algo antiguo. Un TTL demasiado corto provoca muchos fallos; uno demasiado largo, datos desfasados.
- Invalidación explícita: cuando el dato cambia, el código borra o actualiza la entrada. Es más preciso, pero exige acordarse de hacerlo en todos los sitios donde el dato cambia.
La métrica que dice si una caché funciona es la tasa de aciertos:
Una página de producto cuesta 40 ms de CPU generarla con sus consultas, y leerla de una caché en memoria cuesta 1 ms. Recibe 1.000.000 de visitas al día.
- Sin caché: 1.000.000 × 40 ms = 40.000 s, unas 11 horas de CPU al día.
- Con caché y 95 % de aciertos: 950.000 × 1 ms + 50.000 × (40 + 1) ms = 950 s + 2.050 s = 3.000 s, 50 minutos de CPU.
- Con caché y 20 % de aciertos: 200.000 × 1 ms + 800.000 × 41 ms = 200 s + 32.800 s ≈ 9,2 horas de CPU.
4 // Cuándo no compensa
La caché no es una solución universal. Conviene pensárselo dos veces cuando:
- Los datos cambian constantemente: la bolsa en tiempo real, el estado de un pedido que se actualiza cada segundo. El TTL tendría que ser tan corto que casi todo serían fallos.
- Cada petición es distinta: búsquedas con infinitas combinaciones de filtros o contenido muy personalizado. Casi nunca se repite una clave.
- Calcular es más barato que guardar: si generar el resultado cuesta microsegundos, la gestión de la caché puede costar más que el cálculo.
- La caché crece sin límite: sin un tamaño máximo y una política de expulsión (por ejemplo, eliminar lo menos usado recientemente, LRU), la memoria sólo crece.
Otro riesgo es la estampida: cuando caduca una entrada muy popular, cientos de peticiones simultáneas detectan el fallo y se ponen todas a recalcular lo mismo a la vez. Se evita dejando que sólo una recalcule mientras las demás esperan o sirven el valor anterior.
Cachea lo que se pide mucho, cambia poco y cuesta calcular, y hazlo en la capa más cercana al usuario que puedas. Mide la tasa de aciertos: sin ella no sabes si tu caché ahorra energía o sólo ocupa memoria.