Caché: las cuatro trampas que nadie anticipa
Poner caché es fácil. Lo difícil es convivir con las cuatro consecuencias que crea, y las cuatro tienen solución conocida.
Phil Karlton dijo que solo existen dos cosas difíciles en computación: la invalidación de caché y ponerle nombre a las cosas. El chiste envejeció bien porque la primera mitad sigue siendo verdad.
La caché es guardar el resultado caro para no recalcularlo. La idea es trivial. Lo que no es trivial es decidir cuándo ese resultado dejó de valer, y qué le pasa a tu sistema en el instante en que muchos de ellos dejan de valer a la vez.
Dónde cachear
De lo más cerca del usuario a lo más lejos:
- NavegadorgratisControlado por cabecera HTTP. La capa más infrautilizada.
- CDNbordeSirve cerca del usuario, con la directiva s-maxage.
- Local de la aplicaciónnanosegundosEn memoria, en el proceso. Cada instancia tiene la suya, y divergen.
- Distribuidauna ida a la redRedis, Memcached. Compartida y consistente entre instancias.
- De la baseno controladaBuffer pool y page cache. Explica que la segunda ejecución sea más rápida.
Navegador. Gratis, rápido, y lo controlas por cabecera HTTP. Es la capa más infrautilizada.
CDN. Sirve contenido desde el borde, cerca del usuario. También controlada por cabecera, con la
directiva s-maxage.
Caché local de la aplicación. En memoria, en el propio proceso. Latencia de nanosegundos. Problema: cada instancia tiene la suya, y divergen.
Caché distribuida. Redis, Memcached. Compartida entre instancias, consistente entre ellas, con el coste de una ida a la red.
Caché de la base de datos. Buffer pool, page cache del sistema operativo. No la controlas directamente, pero explica por qué la segunda ejecución de la misma query es mucho más rápida.
Las tres estrategias
Cache-aside es la más común: la aplicación busca en la caché; si no encuentra, va a la base y graba en la caché. Simple, lo controlas todo, y la caché puede caerse sin derribar la aplicación.
Write-through graba en la caché y en la base al mismo tiempo. Más consistente, escritura más lenta.
Write-behind graba en la caché y en la base después, por lotes. Rápido y arriesgado: si se cae antes de descargar, perdiste escrituras.
Para la mayoría de los casos, cache-aside es la respuesta correcta. Las otras dos resuelven problemas específicos y traen sus propios modos de fallo.
Trampa 1: dato viejo
El TTL es la expresión numérica de tu tolerancia a estar equivocado.
No existe un TTL correcto en abstracto. Existe la respuesta a: "¿cuánto tiempo puede estar desactualizado este dato sin que a nadie le importe?".
Precio de producto: segundos. Nombre de categoría: horas. Configuración de feature: minutos. Perfil del usuario logueado: ese es el difícil, porque el propio usuario lo nota al instante, y ahí la respuesta es invalidar en la escritura, no confiar en el TTL.
El error común es elegir un TTL único para todo, normalmente cinco minutos, porque era lo que estaba en el ejemplo de la documentación.
Trampa 2: estampida de expiración
Poblaste la caché en un deploy, con TTL de una hora. Una hora después, todo expira en el mismo instante, y todo el tráfico golpea la base de una vez.
La causa es la sincronización. La corrección es el jitter: añade una variación aleatoria al TTL, tipo entre 50 y 70 minutos en vez de 60 exactos. Eso esparce las expiraciones.
Es la misma idea del jitter en los reintentos, y por el mismo motivo: los eventos sincronizados crean picos que el sistema no aguanta, aun teniendo capacidad media de sobra.
Trampa 3: desbandada
Una clave popular expira. Mil peticiones simultáneas lo descubren en el mismo milisegundo y todas van a recalcular lo mismo.
El nombre es thundering herd, y la solución es dejar que solo una recalcule:
- Un lock por clave: quien coja el lock recalcula; los otros esperan o sirven el valor viejo.
- O recomputación anticipada: cuando falte poco para expirar, una petición sorteada renueva en segundo plano mientras las demás siguen sirviendo el valor actual.
La segunda es mejor, porque nadie espera.
Trampa 4: caché negativa
Alguien consulta un id que no existe. No lo encuentras y no guardas nada. La siguiente consulta al mismo id inexistente golpea la base otra vez.
Si eso viene en volumen (y viene, porque así es como se comportan los scrapers y los tests automatizados), tienes una ruta que ignora completamente la caché.
Guarda también el negativo, con TTL corto. Y si el volumen es realmente grande, un filtro de Bloom delante responde "definitivamente no existe" sin tocar siquiera la caché.
La caché como dependencia crítica
Un cache-aside bien implementado degrada: sin caché, todo se pone más lento, y sigue funcionando. Eso exige que el camino hacia la base siga existiendo y que tu base aguante la carga sin la caché, aunque sea con descarte de carga.
El número que importa
Monitoriza la tasa de acierto por clave o por prefijo, no solo la global.
Por debajo del ochenta por ciento en caché de lectura, una de dos cosas está mal: la clave es demasiado granular, o el TTL es demasiado corto para el patrón de acceso. En los dos casos estás pagando la complejidad y la inconsistencia sin recibir el beneficio.
Y monitoriza también la tasa de invalidación. Si es alta, estás gastando más tiempo invalidando que sirviendo.
Resumen práctico
La caché cambia frescura por velocidad. El trabajo de ingeniería no es encender la caché; es decidir explícitamente cuánta frescura aceptas perder, por clave, y después defender el sistema contra las cuatro formas conocidas de que eso salga mal.
Lee esto después
- IngenieríaPaso 4Sharding: una guía de decisiónEl sharding es la decisión más irreversible de un sistema de datos. Esta es la guía para decidir si lo necesitas y, si lo necesitas, cómo elegir la clave.Leer artículo
- IngenieríaPaso 3Índice, EXPLAIN y la query lentaCómo funciona el índice por dentro, por qué el orden de las columnas lo decide todo, y cómo leer un plan de ejecución para saber qué hacer.Leer artículo
- IngenieríaPaso 1Latencia, rendimiento y percentiles: la guía que zanja la discusiónLa media de tu latencia está mintiendo. Los tres conceptos que convierten 'el sistema está lento' en una frase con número, endpoint y percentil.Leer artículo