Estrategias de almacenamiento en caché - Amazon ElastiCache

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Estrategias de almacenamiento en caché

En el siguiente tema, encontrará estrategias para completar y mantener la caché.

Las estrategias que implemente para completar y mantener su caché dependen del tipo de datos que va a almacenar en su caché, así como de los patrones de acceso a dichos datos. Por ejemplo, probablemente no quiera utilizar la misma estrategia para una tabla de clasificación de los 10 mejores jugadores de un sitio de juegos y noticias de moda. En el resto de esta sección, se analizan las distintas estrategias comunes de mantenimiento de caché, junto con sus ventajas e inconvenientes.

Carga diferida

Como su nombre indica, la carga diferida es una estrategia de almacenamiento en caché que carga los datos en la caché solo cuando es necesario. Funciona como se describe a continuación.

Amazon ElastiCache es un almacén de valor de clave en memoria que se sitúa entre su aplicación y el almacén de datos (base de datos) al que accede. Siempre que su aplicación solicite datos, primero realizará una solicitud a la caché de ElastiCache. Si los datos existen en la caché y son actuales, ElastiCache devuelve los datos a su aplicación. Si los datos no existen en la caché o se han vencido, la aplicación solicita los datos del almacén de datos. A continuación, el almacén de datos devuelve los datos a su aplicación. Luego, su aplicación escribe los datos que recibió del almacén en la caché. De esta forma, se pueden recuperar más rápidamente la próxima vez que se los soliciten.

Un acierto de caché se produce cuando los datos se encuentran en la caché y no han vencido:

  1. La aplicación solicita los datos a la caché.

  2. La caché devuelve los datos a la aplicación.

Un error de caché se produce cuando los datos no se encuentran en la caché y han vencido:

  1. La aplicación solicita los datos a la caché.

  2. La caché no dispone de los datos solicitados, por lo que devuelve null.

  3. La aplicación solicita los datos a la base de datos y los recibe.

  4. La aplicación actualiza la caché con los datos nuevos.

Ventajas y desventajas de la carga diferida

Las ventajas de la carga diferida son las siguientes:

  • Solo se almacenan en la caché los datos solicitados.

    Dado que nunca se solicita la mayoría de los datos, la carga diferida evita completar la caché con datos que no se solicitan.

  • Los errores de nodo no son fatales para su aplicación.

    Cuando se produce un error en un nodo y se reemplaza por un nodo nuevo y vacío, la aplicación sigue funcionando, aunque con mayor latencia. Cuando se realizan solicitudes al nodo nuevo, cada error de caché da como resultado una consulta de la base de datos. Al mismo tiempo, la copia de datos se agrega a la caché para que las solicitudes posteriores se recuperen de la caché.

Las desventajas de la carga diferida son las siguientes:

  • Existe una penalización de errores de caché. Cada error de caché genera tres acciones:

    1. Solicitud inicial de los datos a la caché

    2. Consulta de los datos en la base de datos

    3. Escritura de los datos en la caché

    Estos errores pueden provocar un retraso significativo en la obtención de los datos en la aplicación.

  • Datos obsoletos.

    Si los datos se escriben en la caché solo cuando se produce un error de caché, los datos de la caché pueden quedar obsoletos. Este resultado se produce porque no hay actualizaciones en la caché cuando se cambian los datos en la base de datos. Para solucionar este problema, puede utilizar las estrategias Escritura indirecta y Agregar TTL.

Ejemplo de seudocódigo de carga diferida

El siguiente ejemplo es un seudocódigo de lógica de carga diferida.

// ***************************************** // function that returns a customer's record. // Attempts to retrieve the record from the cache. // If it is retrieved, the record is returned to the application. // If the record is not retrieved from the cache, it is // retrieved from the database, // added to the cache, and // returned to the application // ***************************************** get_customer(customer_id) customer_record = cache.get(customer_id) if (customer_record == null) customer_record = db.query("SELECT * FROM Customers WHERE id = {0}", customer_id) cache.set(customer_id, customer_record) return customer_record

Para este ejemplo, el código de aplicación que obtiene los datos es el siguiente.

customer_record = get_customer(12345)

Escritura indirecta

La estrategia de escritura indirecta agrega o actualiza los datos de la caché siempre que se escriben datos en la base de datos.

Ventajas y desventajas de la escritura indirecta

Las ventajas de la escritura indirecta son las siguientes:

  • Los datos de la caché nunca quedan obsoletos.

    Dado que los datos de la caché se actualizan cada vez que se escriben en la base de datos, estos siempre se mantienen actualizados.

  • Penalización de escritura frente a penalización de lectura.

    Toda operación de escritura implica dos acciones:

    1. Una operación de escritura en la caché

    2. Una operación de escritura en la base de datos

    Estas acciones añaden latencia al proceso. Dicho esto, los usuarios finales suelen ser más tolerantes con la latencia a la hora de actualizar datos que con la latencia a la hora de recuperar datos. Existe un sentido inherente que apunta a que las actualizaciones conllevan más trabajo y, por lo tanto, requieren mayor tiempo.

Las desventajas de la escritura indirecta son las siguientes:

  • Pérdida de datos.

    Si pone en marcha un nodo nuevo, ya sea debido a un error de nodo o a una operación de escalado horizontal, existen datos que se perderán. Estos datos siguen faltando hasta que se agregan o actualizan en la base de datos. Puede minimizar esto al implementar una carga diferida con escritura indirecta.

  • Pérdida de caché.

    La mayoría de los datos nunca se leen, lo cual es un desperdicio de recursos. Al agregar un valor de periodo de vida (TTL), puede minimizar el desperdicio de espacio.

Ejemplo de seudocódigo de escritura indirecta

El siguiente ejemplo es un seudocódigo de lógica de escritura indirecta.

// ***************************************** // function that saves a customer's record. // ***************************************** save_customer(customer_id, values) customer_record = db.query("UPDATE Customers WHERE id = {0}", customer_id, values) cache.set(customer_id, customer_record) return success

Para este ejemplo, el código de aplicación que obtiene los datos es el siguiente.

save_customer(12345,{"address":"123 Main"})

Agregar TTL

La carga diferida da lugar a que los datos queden obsoletos, pero no falla con nodos vacíos. La escritura diferida mantiene los datos siempre actualizados, pero puede fallar con nodos vacíos y puede llenar la caché con datos superfluos. Al agregar un valor de periodo de vida (TTL) a cada escritura, puede tener las ventajas de cada estrategia. Al mismo tiempo, puede evitar en gran medida saturar la memoria caché con datos adicionales.

El periodo de vida (TTL) es un valor entero que especifica el número de segundos hasta que venza la clave. Memcached especifica este valor en segundos. Cuando una aplicación intenta leer una clave vencida, la trata como si no se encontrara la clave. La base de datos se consulta para la clave y se actualiza la caché. Este enfoque no garantiza que un valor no se encuentre obsoleto. Sin embargo, evita que los datos queden demasiado obsoletos y se actualizan los valores de la caché con frecuencia desde la base de datos.

Para obtener más información, consulte el comando set de Memcached.

Ejemplos de seudocódigo de TTL

El siguiente ejemplo es un seudocódigo de lógica de escritura indirecta con TTL.

// ***************************************** // function that saves a customer's record. // The TTL value of 300 means that the record expires // 300 seconds (5 minutes) after the set command // and future reads will have to query the database. // ***************************************** save_customer(customer_id, values) customer_record = db.query("UPDATE Customers WHERE id = {0}", customer_id, values) cache.set(customer_id, customer_record, 300) return success

El siguiente ejemplo es un seudocódigo de lógica de carga diferida con TTL.

// ***************************************** // function that returns a customer's record. // Attempts to retrieve the record from the cache. // If it is retrieved, the record is returned to the application. // If the record is not retrieved from the cache, it is // retrieved from the database, // added to the cache, and // returned to the application. // The TTL value of 300 means that the record expires // 300 seconds (5 minutes) after the set command // and subsequent reads will have to query the database. // ***************************************** get_customer(customer_id) customer_record = cache.get(customer_id) if (customer_record != null) if (customer_record.TTL < 300) return customer_record // return the record and exit function // do this only if the record did not exist in the cache OR // the TTL was >= 300, i.e., the record in the cache had expired. customer_record = db.query("SELECT * FROM Customers WHERE id = {0}", customer_id) cache.set(customer_id, customer_record, 300) // update the cache return customer_record // return the newly retrieved record and exit function

Para este ejemplo, el código de aplicación que obtiene los datos es el siguiente.

save_customer(12345,{"address":"123 Main"})
customer_record = get_customer(12345)

Temas relacionados de