Las estrategias de reemplazo determinan qué elementos se eliminarán de la caché cuando haya falta de espacio, logrando un equilibrio entre rendimiento y eficiencia en el uso de recursos. En este artículo, analizaremos 7 estrategias de reemplazo de caché populares, los principios de su funcionamiento, sus ventajas y desventajas.
El almacenamiento en caché es una técnica que acelera el funcionamiento de las aplicaciones, reduce la carga en la base de datos y mejora la experiencia del usuario. Sin embargo, la memoria caché es limitada y no es posible almacenar todo en ella. Las estrategias de reemplazo de caché (o algoritmos de caché) determinan qué elementos se eliminarán de la caché cuando el espacio se acabe. Aquí veremos 7 estrategias principales.
#1. Reemplazo del elemento menos recientemente usado (LRU)
El algoritmo de reemplazo LRU (Least Recently Used) elimina el elemento que menos se ha usado recientemente. Se supone que si hace tiempo que no se accede a un elemento, la probabilidad de que se use en el futuro cercano es baja.

Cómo funciona LRU:
- Seguimiento del tiempo de acceso: LRU recuerda cuándo se usó por última vez cada elemento de la caché. Para ello, a menudo se utiliza:
- Una lista doblemente enlazada: para controlar el orden de los elementos.
- Una tabla hash: para una búsqueda rápida de elementos.
- Elemento encontrado en la caché: Si se encuentra un elemento en la caché, se convierte en el más recientemente usado. En una lista doblemente enlazada, se mueve al principio.
- Elemento no encontrado en la caché: Si hay espacio libre en la caché, el nuevo elemento simplemente se agrega. Si la caché está llena, se elimina el elemento del final de la lista, ya que es el menos solicitado.
Ejemplo de funcionamiento de LRU con una caché de 3 elementos:
- Estado inicial: la caché está vacía.
- Agregamos A: [A].
- Agregamos B: [A, B].
- Agregamos C: [A, B, C] (la caché está llena).
- Usamos A: [B, C, A] (A se convierte en el recientemente usado).
- Agregamos D: [C, A, D] – B se elimina como el menos solicitado.
Ventajas de LRU:
- Es fácil de implementar y se usa ampliamente.
- Es eficiente para consultas frecuentes: los datos que se usan con frecuencia permanecen garantizados en la caché.
- Es ideal para almacenar en caché páginas web y solicitudes de API.
Desventajas de LRU:
- Es necesario almacenar metadatos sobre el orden de uso.
- Con una caché grande, la gestión del orden puede consumir muchos recursos.
- Asume que los usuarios actúan de acuerdo con patrones que permanecen más o menos invariables.
#2. Reemplazo del elemento menos frecuentemente usado (LFU)
El algoritmo LFU (Least Frequently Used) elimina de la caché el elemento con la menor frecuencia de acceso, asumiendo que si un elemento se usó poco antes, la probabilidad de que se use en el futuro es baja.

Cómo funciona LFU:
- Seguimiento de la frecuencia de acceso: Para cada elemento en la caché se guarda un contador de accesos, que aumenta con cada acceso.
- Elemento encontrado en la caché: Cuando se accede a un elemento, su frecuencia aumenta en 1.
- Elemento no encontrado en la caché: Si hay espacio libre, se agrega un nuevo elemento con una frecuencia de 1. Si no hay espacio, se elimina el elemento con la menor frecuencia de acceso. Si varios elementos tienen la misma frecuencia baja de uso, es necesario aplicar una estrategia adicional, por ejemplo, eliminar el más antiguo en cuanto a tiempo de uso o el primero que se agregó.
Ejemplo de funcionamiento de LFU (caché de tamaño 3):
- Estado inicial: la caché está vacía.
- Agregamos A: [A (freq=1)].
- Agregamos B: [A (freq=1), B (freq=1)].
- Agregamos C: [A (freq=1), B (freq=1), C (freq=1)].
- Acceso a A: aumentamos la frecuencia de A, obtenemos [A (freq=2), B (freq=1), C (freq=1)].
- Agregamos D: la caché está llena. Eliminamos el elemento con la frecuencia mínima. B y C tienen la misma frecuencia – 1, por lo tanto, eliminamos B según el principio LRU o FIFO: [A (freq=2), C (freq=1), D (freq=1)].
- Acceso a C: aumentamos la frecuencia de C, obtenemos [A (freq=2), C (freq=2), D (freq=1)].
Ventajas de LFU:
- Es eficiente para patrones predecibles.
- Mantiene bien los elementos más solicitados.
Desventajas de LFU:
- Alto consumo de memoria (es necesario almacenar contadores de frecuencia para cada elemento).
- El aumento de los contadores con cada acceso puede reducir el rendimiento.
- Mala adaptabilidad: conserva elementos que fueron populares hace tiempo, pero que ya no son necesarios.
#3. “Primero en entrar, primero en salir” (FIFO)
FIFO (First In, First Out) es una estrategia de gestión de caché en la que se elimina primero el elemento que se agregó antes que los demás, independientemente de la frecuencia con la que se haya usado. Este método supone que los elementos más antiguos serán menos solicitados cuando la caché se llene.

Cómo funciona FIFO:
- Un nuevo elemento se agrega al final de la cola.
- Elemento encontrado en la caché: Si se encuentra el elemento solicitado en la caché, el orden de la cola permanece inalterado. A diferencia de otras estrategias, FIFO no tiene en cuenta el tiempo o la cantidad de accesos.
- Elemento no encontrado en la caché: Si hay espacio en la caché, el nuevo elemento se agrega al final de la cola. Si la caché está llena, se elimina el elemento más antiguo (del principio de la cola) para liberar espacio.
Ejemplo de funcionamiento de FIFO con una caché de capacidad 3:
- Agregamos A: [A]
- Agregamos B: [A, B]
- Agregamos C: [A, B, C]
- Agregamos D: [B, C, D] (A se elimina porque fue el primero en agregarse)
- Accedemos a B: [B, C, D] (el orden no cambia)
- Agregamos E: [C, D, E] (B se elimina como el elemento más antiguo restante)
Ventajas de FIFO:
- El algoritmo es fácil de implementar mediante la estructura de datos de cola.
- No es necesario realizar un seguimiento de la frecuencia o la antigüedad de uso de los elementos.
- La eliminación siempre se produce según el mismo principio: el elemento más antiguo es el primero en salir.
Desventajas de FIFO:
- Ignora la frecuencia de uso (los elementos solicitados pueden eliminarse, lo que reduce la eficiencia de la caché).
- No es óptimo para la mayoría de los escenarios: en los sistemas reales, normalmente es más importante tener en cuenta los últimos accesos (LRU) o la frecuencia (LFU).
#4. Reemplazo aleatorio (RR)
El reemplazo aleatorio RR (Random Replacement) es la estrategia de gestión de caché más simple, en la que, cuando la caché está llena, para liberar espacio se elimina un elemento aleatorio, independientemente de la frecuencia de uso o del momento de la adición.

Cómo funciona RR:
- Adición de un elemento: Si hay espacio en la caché, el nuevo elemento simplemente se agrega.
- Elemento encontrado en la caché: Si se encuentra el elemento solicitado en la caché, se usa sin cambios en el orden o la composición de la caché.
- Elemento no encontrado en la caché: Si el elemento no está presente y la caché está llena:
- Se elimina un elemento aleatorio.
- El nuevo elemento ocupa el espacio liberado.
Ejemplo de funcionamiento de RR con una caché de 3 elementos:
- Agregamos A: [A].
- Agregamos B: [A, B].
- Agregamos C: [A, B, C].
- Agregamos D: [B, C, D] (A se elimina aleatoriamente).
- Agregamos E: [C, E, D] (B se elimina aleatoriamente).
Ventajas de RR:
- Sencillez de implementación: no es necesario tener en cuenta la frecuencia o el tiempo de acceso.
- Carga mínima.
- Adecuado para patrones de acceso caóticos o impredecibles.
Desventajas de RR:
- Los elementos importantes y que se usan con frecuencia pueden eliminarse aleatoriamente.
- Ineficiencia en patrones de acceso estables: si se solicitan determinados datos con regularidad, su eliminación aleatoria reduce la eficiencia de la caché.
- Debido al enfoque aleatorio de la eliminación, la caché puede conservar elementos innecesarios.
#5. Reemplazo del elemento más recientemente usado (MRU)
“Último usado, primero eliminado” es una estrategia de gestión de caché opuesta a LRU: cuando la caché está llena y es necesario liberar espacio, MRU (Most Recently Used) elimina el elemento que se usó más recientemente. La lógica aquí es que el elemento usado recientemente pudo haber sido necesario solo temporalmente y no se necesitará en el futuro cercano.

Cómo funciona MRU:
- Adición de un elemento: El nuevo elemento se agrega a la caché y se marca como el último usado (MRU).
- Elemento encontrado en la caché: Si se encuentra el elemento solicitado, se convierte en el último usado (MRU).
- Elemento no encontrado en la caché: Si hay espacio libre, el nuevo elemento simplemente se agrega. Si la caché está llena, se elimina el elemento último usado (MRU) para liberar espacio para el nuevo.
Ejemplo de funcionamiento de MRU con una caché de 3 elementos:
- Agregamos A: [A]
- Agregamos B: [A, B]
- Agregamos C: [A, B, C]
- Accedemos a C: [A, B, C] (C es el más recientemente usado)
- Agregamos D: [A, B, D] (C se elimina porque era MRU)
- Accedemos a B: [A, B, D] (B es ahora MRU)
- Agregamos E: [A, D, E] (B se elimina porque era MRU)
Ventajas de MRU:
- Es eficiente cuando los datos valiosos son registros más antiguos, y los recientes son necesarios temporalmente.
- Sencillez de implementación: requiere un seguimiento mínimo del orden de uso.
- Fácil de implementar con una pila.
Desventajas de MRU:
- No es adecuado para la mayoría de los escenarios: en muchos sistemas, los datos usados recientemente siguen siendo solicitados.
- Si los patrones de acceso se repiten, la caché perderá los elementos necesarios.
- Es menos eficiente que los algoritmos LRU y LFU.
#6. Tiempo de vida del elemento en la caché (TTL)
Time to Live (TTL) es una estrategia de gestión de caché en la que a cada elemento se le asigna un tiempo de vida. Una vez que expira este plazo, el elemento se elimina automáticamente de la caché, independientemente de si se usó o no. TTL se usa activamente en sistemas de almacenamiento en caché, por ejemplo, en Redis y Memcached.

Cómo funciona TTL:
- Adición de un elemento: Al agregar un elemento, se le asigna un TTL (por ejemplo, 5 segundos). El tiempo de expiración se calcula como el tiempo actual + TTL.
- Acceso a un elemento: Si se encuentra un elemento, pero el TTL ha expirado, el elemento se elimina de la caché. Si el TTL aún está vigente, el elemento se devuelve.
- Eliminación de un elemento: Los elementos se eliminan:
- Al acceder (limpieza diferida de la caché): si el plazo ha expirado durante la solicitud.
- Periódicamente (limpieza programada): mediante tareas en segundo plano.
Ejemplo de funcionamiento de TTL con una caché (TTL = 5 segundos):
- Agregamos A (TTL = 5s): [A (5s)]
- Agregamos B (TTL = 10s): [A (5s), B (10s)]
- Después de 6 segundos: [B (quedan 4s)] (A se elimina porque el TTL ha expirado)
- Agregamos C (TTL = 5s): [B (4s), C (5s)]
- Accedemos a A: el elemento no se encuentra, ya que A ya se ha eliminado.
Ventajas de TTL:
- Actualidad de los datos: elimina automáticamente los elementos obsoletos.
- Facilidad de configuración: el TTL se puede asignar fácilmente al agregar datos.
- No requiere métricas complejas de acceso o frecuencia de uso.
- Adecuado para sistemas donde la frescura de la información es importante (por ejemplo, almacenamiento en caché de precios, datos de API).
Desventajas de TTL:
- Plazo fijo: los elementos pueden eliminarse, incluso si siguen siendo solicitados.
- Los elementos con un TTL grande ocupan memoria, incluso si ya no son necesarios.
- No tiene en cuenta los patrones o la frecuencia de uso.
#7. Almacenamiento en caché de dos niveles
El almacenamiento en caché de dos niveles TTC (Two-Tiered Caching) combina dos niveles de caché: uno local (en memoria) y otro remoto (distribuido o compartido):
- Caché local: el primer nivel (“caché caliente”), que proporciona acceso ultrarrápido a los datos que se usan con frecuencia.
- Caché remota: el segundo nivel (“caché fría”), que se usa para los datos que no están en la caché local, pero que deben proporcionarse más rápido que desde la base de datos.

Cómo funciona la caché de dos niveles:
- Caché local (primer nivel):
- Se encuentra en el mismo servidor donde se ejecuta la aplicación (la mayoría de las veces, en la memoria RAM).
- Proporciona el acceso más rápido posible a los datos que se usan con frecuencia y reduce la carga en el servidor.
- Ejemplos: estructuras de datos en memoria – tablas hash, caché LRU. Bibliotecas y marcos – Guava Cache (Java).
- Caché remota (segundo nivel):
- Se encuentra en un servidor o clúster de servidores independiente.
- Es una caché compartida para varias aplicaciones.
- El acceso a los datos es más lento que en la caché local.
- Permite almacenar en caché grandes volúmenes de datos.
Ejemplo: sistemas de almacenamiento en caché distribuidos (Redis, Memcached).
Proceso de funcionamiento de la caché de dos niveles:
- Solicitud del cliente → verificación de la caché local:
- Si se encuentran los datos, se devuelven al instante.
- Si no se encuentran, pasamos al siguiente paso.
- Verificación de la caché remota:
- Si se encuentran los datos, se devuelven y se guardan en la caché local para futuras solicitudes.
- Si no se encuentran, pasamos al siguiente paso.
- Solicitud a la base de datos principal:
- Los datos obtenidos se devuelven al cliente.
- Al mismo tiempo, los datos se guardan en las cachés local y remota.
Ventajas del almacenamiento en caché de dos niveles:
- Acceso rápido: la caché local proporciona una respuesta instantánea para los datos que se solicitan con frecuencia.
- Escalabilidad: la caché remota permite almacenar más datos y compartirlos entre servidores.
- Reducción de la carga en la base de datos: la mayoría de las solicitudes se atienden desde la caché, lo que reduce el número de accesos a la base de datos.
- Tolerancia a fallos: si la caché local falla, los datos se obtendrán de la caché remota.
Desventajas del almacenamiento en caché de dos niveles:
- Complejidad de gestión: se requiere sincronización entre los dos niveles de caché.
- Problema de datos obsoletos: si los datos se han actualizado en una caché, pero no en la otra, el cliente puede recibir una versión anterior.
- Retrasos al trabajar con la caché remota: las solicitudes al segundo nivel se ejecutan más lentamente debido a los retrasos de red.
Resumen
La elección de la estrategia de reemplazo de caché depende de las características de la aplicación, el tipo de carga y los patrones de acceso a los datos: LRU y LFU son adecuados para escenarios con solicitudes predecibles, FIFO y RR son útiles en sistemas simples, TTL mantiene eficazmente la actualidad de los datos, y el almacenamiento en caché de dos niveles proporciona un equilibrio entre velocidad y escalabilidad.
Al usar una estrategia adecuada o una combinación de ellas, se puede lograr un uso más eficiente de la memoria y mejorar la experiencia del usuario.