Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Este patrón carga datos a petición en una memoria caché desde un almacén de datos. Use este patrón para mejorar el rendimiento y ayudar a mantener la coherencia entre los datos de una caché y los datos de un almacén de datos subyacente.
Contexto y problema
Las aplicaciones usan una caché para mejorar el rendimiento del acceso repetido a la información de un almacén de datos. Pero los datos almacenados en caché no siempre pueden ser coherentes con el almacén de datos. Las aplicaciones deben implementar una estrategia que mantenga los datos en la memoria caché lo más actualizados posible. La estrategia también debe detectar cuándo los datos almacenados en caché se vuelven obsoletos y controlarlos adecuadamente.
Solución
Muchos sistemas comerciales de almacenamiento en caché proporcionan operaciones de lectura pasante y escritura pasante o escritura diferida. En estos sistemas, una aplicación recupera datos mediante la referencia a la caché. Si los datos no están en la memoria caché, la aplicación la recupera del almacén de datos y la agrega a la memoria caché. El sistema escribe automáticamente los cambios realizados en los datos almacenados en caché en el almacén de datos.
En el caso de las memorias caché que no proporcionan esta funcionalidad, las aplicaciones que usan la caché deben mantener los datos.
Una aplicación puede emular la funcionalidad del almacenamiento en caché de lectura directa mediante la implementación del patrón Cache-Aside. Esta estrategia carga datos en la caché a petición. En el diagrama siguiente se usa el patrón Cache-Aside para almacenar datos en la memoria caché.
La aplicación determina si un elemento reside actualmente en la memoria caché intentando leer desde la memoria caché.
Si el elemento no está en la memoria caché, también conocido como error de caché, la aplicación recupera el elemento del almacén de datos.
La aplicación agrega el elemento a la memoria caché y, a continuación, lo devuelve al autor de la llamada.
Si una aplicación actualiza información, puede seguir la estrategia de escritura directa haciendo la modificación en el almacén de datos e invalidando el elemento correspondiente en la memoria caché.
Cuando se vuelve a necesitar el elemento, el patrón Cache-Aside recupera los datos actualizados del almacén de datos y lo agrega a la memoria caché.
Problemas y consideraciones
Tenga en cuenta los siguientes puntos a medida que decida cómo implementar este patrón:
Duración de los datos almacenados en caché: Muchas memorias caché usan una directiva de expiración para invalidar los datos y quitarlos de la memoria caché si no se tiene acceso a ella durante un período establecido. Para que la estrategia de caché lateral sea efectiva, asegúrese de que la política de expiración coincida con el patrón de acceso de las aplicaciones que utilizan los datos. No haga que el período de expiración sea demasiado corto porque la expiración prematura puede hacer que las aplicaciones recuperen continuamente datos del almacén de datos y agréguelos a la memoria caché. Del mismo modo, no haga que el período de expiración sea tan largo que los datos almacenados en caché se vuelvan obsoletos. El almacenamiento en caché funciona mejor para datos o datos relativamente estáticos que las aplicaciones leen con frecuencia.
Expulsar datos: La mayoría de las memorias caché tienen un tamaño limitado en comparación con el almacén de datos donde se originan los datos. Si la memoria caché supera su límite de tamaño, expulsa los datos. La mayoría de las memorias caché adoptan una directiva que se usa menos recientemente para seleccionar elementos para la expulsión, pero algunas permiten la personalización.
Configuración: Puede configurar el comportamiento de la caché globalmente o por elemento almacenado en caché. Es posible que una única directiva de expulsión global no se adapte a todos los elementos. Si un elemento es caro de recuperar, configure el elemento de caché individualmente. En esta situación, tiene sentido mantener el elemento en la memoria caché, incluso si se accede a ellos con menos frecuencia que los elementos más baratos.
Priming the cache: Muchas soluciones rellenan previamente la memoria caché con datos que es probable que una aplicación requiera como parte del procesamiento de inicio. El patrón Cache-Aside sigue siendo útil cuando algunos de estos datos expiran o se expulsan.
Coherencia: El patrón Cache-Aside no garantiza la coherencia entre el almacén de datos y la memoria caché. Por ejemplo, un proceso externo puede cambiar un elemento en el almacén de datos en cualquier momento. Este cambio no aparece en la memoria caché hasta que el elemento se vuelva a cargar. En un sistema que replica datos entre almacenes de datos, la sincronización frecuente puede dificultar la coherencia.
Almacenamiento en caché local: Una caché puede ser local en una instancia de aplicación y almacenarse en memoria. Cache-aside funciona bien en este entorno si una aplicación accede repetidamente a los mismos datos. Pero una caché local es privada, por lo que diferentes instancias de aplicación pueden tener una copia de los mismos datos almacenados en caché. Estos datos pueden convertirse rápidamente en incoherentes entre las memorias caché, por lo que es posible que tenga que expirar los datos en una caché privada y actualizarlos con más frecuencia. En estos escenarios, considere la posibilidad de usar un mecanismo de almacenamiento en caché compartido o distribuido.
Almacenamiento en caché semántico: Algunas cargas de trabajo pueden beneficiarse de la recuperación de caché en función del significado semántico en lugar de las claves exactas. Este enfoque reduce el número de solicitudes y tokens enviados a los modelos de lenguaje. Use solo el almacenamiento en caché semántico cuando los datos admitan la equivalencia semántica, no corre el riesgo de devolver respuestas no relacionadas y no contiene datos privados y confidenciales. Por ejemplo, "¿Cuál es mi salario neto anual?" es semánticamente similar a "¿Cuál es mi paga neta anual?" Pero si diferentes usuarios formulan estas preguntas, las respuestas deberían diferir. Tampoco debe incluir estos datos confidenciales en la memoria caché.
Cuándo usar este patrón
Use este patrón en los siguientes supuestos:
Una caché no proporciona operaciones nativas de lectura a través y escritura a través.
La demanda de recursos es impredecible. Este patrón permite que las aplicaciones carguen datos a petición. No supone qué datos requiere una aplicación de antemano.
Este patrón podría no ser adecuado cuando:
Los datos son confidenciales o relacionados con la seguridad. Almacenar datos en una memoria caché podría ser inadecuado, especialmente cuando varias aplicaciones o usuarios comparten la memoria caché. Recupere siempre este tipo de datos del origen principal.
El conjunto de datos almacenados en caché es estático. Si los datos encajan en el espacio de caché disponible, prime la memoria caché con los datos al iniciarse y aplique una directiva que impida que los datos expiren.
La mayoría de las solicitudes no experimentan un acierto de caché. En esta situación, la sobrecarga de comprobar la caché y cargar datos en ella puede superar las ventajas del almacenamiento en caché.
La información de estado de sesión se almacena en caché en una aplicación web hospedada en una granja de servidores web. En este entorno, evite introducir dependencias basadas en la afinidad de cliente-servidor.
Diseño de cargas de trabajo
Evalúe cómo usar el patrón de Cache-Aside en el diseño de una carga de trabajo para abordar los objetivos y principios descritos en los pilares de Azure Well-Architected Framework. En la tabla siguiente se proporciona una guía sobre cómo este patrón apoya los objetivos de cada pilar.
| Fundamento | Cómo apoya este patrón los objetivos de los pilares |
|---|---|
| Las decisiones de diseño de fiabilidad ayudan a que su carga de trabajo sea resiliente a fallos y garantizan que se recupere a un estado de pleno funcionamiento después de que se produzca un fallo. | El almacenamiento en caché replica los datos. De manera limitada, puede conservar la disponibilidad de los datos a los que se accede con frecuencia si el almacén de datos de origen deja de estar disponible temporalmente. Si la memoria caché no funciona correctamente, la carga de trabajo puede revertir al almacén de datos de origen. - RE:05 Redundancia |
| Eficiencia del rendimiento ayuda a su carga de trabajo a satisfacer eficientemente las demandas mediante optimizaciones en el escalado, los datos y el código. | El almacenamiento en caché mejora el rendimiento de los datos con mucha lectura que cambian con poca frecuencia y toleran cierta obsolescencia. - PE:08 Rendimiento de datos - PE:12 Optimización continua del rendimiento |
Si este patrón introduce concesiones dentro de un pilar, considérelas en relación con los objetivos de los otros pilares.
Ejemplo
Considere la posibilidad de usar Azure Managed Redis para crear una caché distribuida que varias instancias de aplicación puedan compartir.
En el ejemplo siguiente se usa el cliente StackExchange.Redis , que es una biblioteca cliente de Redis escrita para .NET. Para conectarse a una instancia de Azure Managed Redis, llame al método estático ConnectionMultiplexer.Connect y pase la cadena de conexión. El método devuelve un elemento ConnectionMultiplexer que representa la conexión.
Una manera de compartir una ConnectionMultiplexer instancia en la aplicación es tener una propiedad estática que devuelva una instancia conectada, similar al ejemplo siguiente. Este enfoque proporciona una manera segura para hilos de inicializar solo una instancia conectada.
private static ConnectionMultiplexer Connection;
// Redis connection string information
private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
{
string cacheConnection = ConfigurationManager.AppSettings["CacheConnection"].ToString();
return ConnectionMultiplexer.Connect(cacheConnection);
});
public static ConnectionMultiplexer Connection => lazyConnection.Value;
El GetMyEntityAsync método del ejemplo siguiente muestra una implementación del patrón Cache-Aside. Este método recupera un objeto de la memoria caché mediante el enfoque de lectura a través.
El método identifica un objeto mediante un identificador entero como clave. Intenta recuperar un elemento de la memoria caché mediante esta clave. Si la memoria caché contiene un elemento coincidente, devuelve el elemento. Si la memoria caché no contiene una coincidencia, el GetMyEntityAsync método recupera el objeto de un almacén de datos, lo agrega a la memoria caché y, a continuación, lo devuelve. En este ejemplo se omite el código que lee los datos del almacén de datos porque esa lógica depende del almacén de datos. El elemento almacenado en caché está configurado para que expire para evitar que quede obsoleto si otro servicio o proceso lo actualiza.
// Set five minute expiration as a default
private const double DefaultExpirationTimeInMinutes = 5.0;
public async Task<MyEntity> GetMyEntityAsync(int id)
{
// Define a unique key for this method and its parameters.
var key = $"MyEntity:{id}";
var cache = Connection.GetDatabase();
// Try to get the entity from the cache.
var json = await cache.StringGetAsync(key).ConfigureAwait(false);
var value = string.IsNullOrWhiteSpace(json)
? default(MyEntity)
: JsonConvert.DeserializeObject<MyEntity>(json);
if (value == null) // Cache miss
{
// If there's a cache miss, get the entity from the original store and cache it.
// Code has been omitted because it is data store dependent.
value = ...;
// Avoid caching a null value.
if (value != null)
{
// Put the item in the cache with a custom expiration time that
// depends on how critical it is to have stale data.
await cache.StringSetAsync(key, JsonConvert.SerializeObject(value)).ConfigureAwait(false);
await cache.KeyExpireAsync(key, TimeSpan.FromMinutes(DefaultExpirationTimeInMinutes)).ConfigureAwait(false);
}
}
return value;
}
Nota:
En los ejemplos se usa Azure Managed Redis para acceder al almacén y recuperar información de la memoria caché. Para más información, consulte Creación de una instancia de Azure Managed Redis y Uso de Azure Managed Redis en .NET Core.
El método siguiente UpdateEntityAsync muestra cómo invalidar un objeto en la memoria caché cuando la aplicación cambia el valor. El código actualiza el almacén de datos original y, a continuación, quita el elemento en caché de la caché.
public async Task UpdateEntityAsync(MyEntity entity)
{
// Update the object in the original data store.
await this.store.UpdateEntityAsync(entity).ConfigureAwait(false);
// Invalidate the current cache object.
var cache = Connection.GetDatabase();
var id = entity.Id;
var key = $"MyEntity:{id}"; // The key for the cached object.
await cache.KeyDeleteAsync(key).ConfigureAwait(false); // Delete this key from the cache.
}
Nota:
Es importante el orden de los pasos. Actualice el almacén de datos antes de quitar el elemento de la caché. Si quita primero el elemento almacenado en caché, hay una pequeña ventana de tiempo en la que un cliente podría capturar el elemento antes de actualizar el almacén de datos. En esta situación, la solicitud resulta en un fallo de caché porque el elemento no está en la memoria caché. La falta de caché hace que la aplicación recupere el elemento obsoleto del almacén de datos y vuelva a agregarlo a la memoria caché. Esta secuencia conduce a datos obsoletos en la memoria caché.
Pasos siguientes
Manual de coherencia de datos: en este manual se describen los problemas de coherencia entre los datos distribuidos. También resume cómo una aplicación puede implementar la coherencia final para mantener la disponibilidad de los datos. Las aplicaciones en la nube suelen almacenar datos en varios almacenes de datos y ubicaciones. Debe administrar y mantener eficazmente la coherencia de los datos en este entorno, especialmente debido a problemas de simultaneidad y disponibilidad que pueden surgir.
Uso de Azure Managed Redis como caché semántica: en este tutorial se muestra cómo implementar el almacenamiento en caché semántico mediante Azure Managed Redis.
Recursos relacionados
Patrón de aplicación web confiable: este patrón aplica el patrón de Cache-Aside a las aplicaciones web en la nube.
Guía de almacenamiento en caché: esta guía proporciona más información sobre cómo almacenar en caché los datos en una solución en la nube y los problemas que se deben tener en cuenta al implementar una memoria caché.