Cache-Aside-mönster

Azure Managed Redis

Det här mönstret läser in data på begäran i en cache från ett datalager. Använd det här mönstret för att förbättra prestanda och bidra till att upprätthålla konsekvens mellan data i en cache och data i ett underliggande datalager.

Kontext och problem

Program använder en cache för att förbättra prestanda för upprepad åtkomst till information i ett datalager. Men cachelagrade data kan inte alltid vara konsekventa med datalagret. Program bör implementera en strategi som håller data i cacheminnet så up-to-date som möjligt. Strategin bör också identifiera när cachelagrade data blir inaktuella och hantera dem på rätt sätt.

Lösning

Många kommersiella cachelagringssystem tillhandahåller genomläsning och skrivgenom eller fördröjd skrivning. I de här systemen hämtar ett program data genom att referera till cachen. Om data inte finns i cacheminnet hämtar programmet dem från datalagret och lägger till dem i cacheminnet. Systemet skriver automatiskt alla ändringar som görs i cachelagrade data tillbaka till datalagret.

För cacheminnen som inte tillhandahåller den här funktionen måste de program som använder cacheminnet underhålla data.

En applikation kan emulera funktionaliteten hos read-through-caching genom att implementera Cache-Aside-mönstret. Den här strategin skickar data till cachelagringen på begäran. I följande diagram används mönstret Cache-Aside för att lagra data i cacheminnet.

Diagram som visar användningen av Cache-Aside-mönstret för att läsa och lagra data i cacheminnet.

  1. Programmet avgör om ett objekt för närvarande finns i cacheminnet genom att försöka läsa från cacheminnet.

  2. Om objektet inte finns i cacheminnet, även kallat cachemiss, hämtar programmet objektet från datalagret.

  3. Programmet lägger till objektet i cacheminnet och returnerar det sedan till anroparen.

Om ett program uppdaterar information kan det följa genomskrivningsstrategin genom att ändra datalagret och ogiltigförklara motsvarande objekt i cacheminnet.

När objektet behövs igen hämtar Cache-Aside-mönstret uppdaterade data från datalagret och lägger till dem i cacheminnet.

Problem och överväganden

Tänk på följande när du bestämmer hur du ska implementera det här mönstret:

  • Livslängd för cachelagrade data: Många cacheminnen använder en förfalloprincip för att ogiltigförklara data och ta bort dem från cacheminnet om de inte används under en angiven period. För att göra cache-aside effektivt bör du se till att principen för utgång matchar åtkomstmönstret för applikationer som använder data. Gör inte förfalloperioden för kort eftersom för tidig förfallotid kan göra att program kontinuerligt hämtar data från datalagret och lägger till dem i cacheminnet. På samma sätt ska du inte göra förfalloperioden så lång att cachelagrade data blir inaktuella. Cachelagring fungerar bäst för relativt statiska data eller data som program läser ofta.

  • Ta bort data: De flesta cacheminnen har en begränsad storlek jämfört med datalagret där data kommer. Om cachen överskrider sin storleksgräns avlägsnas data. De flesta cacheminnen använder en minst använda princip för att välja objekt för borttagning, men vissa tillåter anpassningar.

  • Konfiguration: Du kan konfigurera cachebeteende globalt eller per cachelagrat objekt. En enda global borttagningsprincip kanske inte passar alla objekt. Om ett objekt är dyrt att hämta konfigurerar du cacheobjektet individuellt. I den här situationen är det klokt att behålla objektet i cacheminnet, även om det används mindre ofta än billigare objekt.

  • Förbered cacheminnet: Många lösningar fyller i cachen i förväg med data som ett program sannolikt kräver som en del av startbearbetningen. Mönstret Cache-Aside är fortfarande användbart när vissa av dessa data upphör att gälla eller tas bort.

  • Konsekvens: Mönstret Cache-Aside garanterar inte konsekvens mellan datalagret och cacheminnet. En extern process kan till exempel ändra ett objekt i datalagret när som helst. Den här ändringen visas inte i cacheminnet förrän objektet läses in igen. I ett system som replikerar data mellan datalager kan frekvent synkronisering göra konsekvensen utmanande.

  • Lokal cachelagring: En cache kan vara lokal för en programinstans och lagras i minnet. Cache-aside fungerar bra i den här miljön om ett program upprepade gånger kommer åt samma data. Men en lokal cache är privat, så olika programinstanser kan ha en kopia av samma cachelagrade data. Dessa data kan snabbt bli inkonsekventa mellan cacheminnen, så du kan behöva ta bort data i en privat cache och uppdatera den oftare. I dessa scenarier bör du överväga att använda en delad eller distribuerad cachelagringsmekanism.

  • Semantisk cachelagring: Vissa arbetsbelastningar kan dra nytta av cachehämtning baserat på semantisk betydelse snarare än exakta nycklar. Den här metoden minskar antalet begäranden och token som skickas till språkmodeller. Använd endast semantisk cachelagring när data stöder semantisk ekvivalens, inte riskerar att returnera orelaterade svar och inte innehåller privata och känsliga data. Till exempel liknar "Vad är min årliga hemlön?" semantiskt "Vad är min årliga hemlön?" Men om olika användare ställer dessa frågor bör svaren skilja sig åt. Du bör inte heller inkludera dessa känsliga data i cacheminnet.

När du ska använda det här mönstret

Använd det här mönstret i sådana här scenarier:

  • En cache tillhandahåller inte inbyggda read-through och write-through-operationer.

  • Resursbehovet är oförutsägbart. Det här mönstret gör det möjligt för program att läsa in data på begäran. Det förutsätter inte vilka data ett program kräver i förväg.

Det här mönstret kanske inte är lämpligt när:

  • Data är känsliga eller säkerhetsrelaterade. Det kan vara olämpligt att lagra data i en cache, särskilt när flera program eller användare delar cacheminnet. Hämta alltid den här typen av data från den primära källan.

  • Den cachelagrade datauppsättningen är statisk. Om data passar in i det tillgängliga cacheutrymmet skapar du cachen med data vid start och tillämpar en princip som förhindrar att data upphör att gälla.

  • De flesta begäranden får inte en cacheträff. I den här situationen kan kostnaden för att kontrollera cacheminnet och läsa in data i den uppväga fördelarna med cachelagring.

  • Du cachelagrar information om sessionstillstånd i en webbapplikation som hostas i en webbkluster. I den här miljön bör du undvika att införa beroenden baserat på klient-server-tillhörighet.

Design av arbetsbelastning

Utvärdera hur du använder Cache-Aside-mönstret i en arbetsbelastnings design för att uppfylla de mål och principer som beskrivs i Grundpelarna i Azure Well-Architected Framework. Följande tabell innehåller vägledning om hur det här mönstret stöder målen för varje pelare.

Grundpelare Så här stöder det här mönstret pelarmål
Tillförlitlighets designbeslut hjälper din arbetsbelastning att bli motståndskraftig mot funktionsfel och säkerställer att den återställer sig till ett fullständigt fungerande tillstånd när ett fel uppstår. Cachelagring replikerar data. På begränsade sätt kan den bevara tillgängligheten för data som används ofta om ursprungsdatalagret blir tillfälligt otillgängligt. Om cachen inte fungerar kan arbetsbelastningen återgå till ursprungsdatalagret.

- RE:05 Redundans
Prestandaeffektivitet hjälper din arbetsbelastning effektivt uppfylla kraven genom optimering av skalning, data och kod. Cachelagring förbättrar prestandan för läsintensiva data som ändras sällan och tolererar viss inaktuellhet.

- PE:08 Dataprestanda
- PE:12 Kontinuerlig prestandaoptimering

Om detta mönster inför kompromisser inom en pelare bör du överväga dem mot målen för de andra pelarna.

Exempel

Överväg att använda Azure Managed Redis för att skapa en distribuerad cache som flera programinstanser kan dela.

I följande exempel används StackExchange.Redis-klienten , som är ett Redis-klientbibliotek skrivet för .NET. Om du vill ansluta till en Azure Managed Redis-instans anropar du den statiska ConnectionMultiplexer.Connect metoden och skickar anslutningssträngen. Metoden returnerar ConnectionMultiplexer som representerar anslutningen.

Ett sätt att dela en ConnectionMultiplexer instans i ditt program är att ha en statisk egenskap som returnerar en ansluten instans, ungefär som i följande exempel. Det här tillvägagångssättet är ett trådsäkert sätt för att endast initiera en enskild ansluten instans.

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;

Metoden GetMyEntityAsync i följande exempel visar en implementering av Cache-Aside-mönstret. Den här metoden hämtar ett objekt från cachen med hjälp av metoden för genomläsning.

Metoden identifierar ett objekt med hjälp av ett heltals-ID som nyckel. Den försöker hämta ett objekt från cachen med hjälp av den här nyckeln. Om cacheminnet innehåller ett matchande objekt returneras objektet. Om cacheminnet inte innehåller någon matchning GetMyEntityAsync hämtar metoden objektet från ett datalager, lägger till det i cacheminnet och returnerar det sedan. Det här exemplet utelämnar koden som läser data från datalagret eftersom den logiken är beroende av datalagret. Det cachelagrade objektet har konfigurerats att upphöra att gälla för att förhindra att det blir inaktuellt om en annan tjänst eller process uppdaterar det.

// 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;
}

Kommentar

Exemplen använder Azure Managed Redis för att komma åt arkivet och hämta information från cacheminnet. Mer information finns i Skapa en Azure Managed Redis-instans och Använda Azure Managed Redis i .NET Core.

Följande UpdateEntityAsync metod visar hur du ogiltigförklarar ett objekt i cacheminnet när programmet ändrar värdet. Koden uppdaterar den ursprungliga datalagringen och tar sedan bort det cachelagrade objektet från cacheminnet.

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.
}

Kommentar

Stegens ordningsföljd är mycket viktig. Uppdatera datalagringen innan du tar bort objektet från cachen. Om du tar bort det cachelagrade objektet först finns det en liten tidsperiod när en klient kan hämta objektet innan datalagret uppdateras. I det här fallet resulterar hämtningen i en cachemiss eftersom objektet inte finns i cacheminnet. Cachemissen gör att programmet hämtar det inaktuella objektet från datalagret och lägger till det i cacheminnet igen. Den här sekvensen leder till inaktuella data i cacheminnet.

Nästa steg

  • Primer för datakonsekvens: Den här primern beskriver problem med konsekvens mellan distribuerade data. Det sammanfattar också hur ett program kan implementera eventuell konsistens för att upprätthålla tillgängligheten av data. Molnprogram lagrar vanligtvis data över flera datalager och platser. Du måste effektivt hantera och upprätthålla datakonsekvens i den här miljön, särskilt på grund av samtidighets- och tillgänglighetsproblem som kan uppstå.

  • Använda Azure Managed Redis som en semantisk cache: Den här självstudien visar hur du implementerar semantisk cachelagring med hjälp av Azure Managed Redis.