Mönstret Cache-Aside
Läs in data på begäran till en cache från ett datalager. Det här kan förbättra prestanda och även bidra till att upprätthålla konsekvens mellan cachelagrade data och data i det underliggande datalagret.
Kontext och problem
Program använder en cache för att förbättra upprepad åtkomst till information som lagras i ett datalager. Det är dock orealistiskt att förvänta sig att cachelagrade data alltid ska vara konsekventa med datalagret. Program bör implementera en strategi som hjälper till att säkerställa att data i cacheminnet är så up-todatum som möjligt. Strategin bör också kunna identifiera när cachelagrade data blir inaktuella och hantera dem på rätt sätt.
Lösning
Många kommersiella cachelagringssystem har åtgärder för genomläsning och genomskrivning/skrivning bakom. 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. Ändringar av data som lagras i cachen skrivs också automatiskt tillbaka till dataarkivet.
Program som bibehåller data med cachen ansvarar inte för cache som inte har den här funktionen.
Ett program kan emulera funktionerna för cachelagring av genomläsning genom att implementera en strategi för cache-aside. Den här strategin skickar data till cachelagringen på begäran. Bilden illustrerar hur du använder Cache-Aside-mönstret för att lagra data i cachelagringen.
- Programmet avgör om objektet för närvarande lagras i cacheminnet genom att försöka läsa från cacheminnet.
- Om objektet inte är aktuellt i cacheminnet (en cachemiss) hämtar programmet objektet från datalagret.
- Programmet lägger till objektet i cacheminnet och returnerar det sedan till anroparen.
Om ett program uppdaterar information kan det följa en strategi för genomskrivning genom att modifiera datalagringen och ogiltigförklara motsvarande objekt i cachen.
När objektet behövs igen hämtar strategin cache-aside 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 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 cache-aside ska vara effektivt säkerställer du att förfallopolicyn matchar mönstret för åtkomst till program som använder dessa 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. Gör inte heller förfalloperioden för lång, så att de cachelagrade datan med största säkerhet blir inaktuella. Kom ihåg att cachelagring är mest effektivt för relativt statiska data eller data som läses 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 princip som används minst nyligen för att välja objekt som ska avlägsnas, men det kan vara anpassningsbart.
Konfiguration. Cachekonfiguration kan anges både globalt och per cachelagrat objekt. En enda global borttagningsprincip kanske inte passar alla objekt. En konfiguration på ett cacheobjekt kan vara lämplig om ett objekt är dyrt att hämta. I den här situationen är det klokt att behålla objektet i cacheminnet, även om det används mindre ofta än billigare objekt.
Prima cachen. Många lösningar fyller i cachen i förväg med data som ett program förmodligen kommer att behöva som en del av startup-processen. Cache-Aside-mönstret kan fortfarande vara användbart om dessa data utgår eller avlägsnas.
Konsekvens. Konsekvens mellan datalagret och cachen garanteras inte av att Cache-Aside-mönstret implementeras. 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 konsekvens vara en utmaning om synkronisering sker ofta.
Lokal (minnesintern) cachelagring. Cachelagring kan vara lokalt i en programinstans och lagras internt. Cache-aside kan vara användbart i den här miljön om ett program upprepade gånger använder samma data. Däremot är lokala cache privata och anda programinstanser kan ha en kopia vardera av samma cachelagrade data. Dessa data kan snabbt bli inkonsekventa mellan cachen, så det kan vara nödvändigt att ta bort data som lagras i privat cache och uppdatera oftare. I de här scenariona bör du överväga att undersöka användningen av en delad eller distribuerad cachelagringsmekanism.
Semantisk cachelagring. Vissa arbetsbelastningar kan dra nytta av cachehämtning baserat på semantisk betydelse snarare än exakta nycklar. Detta minskar antalet begäranden och token som skickas till språkmodeller. Se till att data som cachelagras drar nytta av semantisk ekvivalens och inte riskerar att returnera orelaterade svar eller innehålla privata och känsliga data. Till exempel liknar "Vad är min årliga hemlön?" semantiskt "Vad är min årliga hemlön?" men om jag tillfrågas av två olika användare så bör svaret inte vara detsamma, och du skulle inte heller vilja inkludera dessa känsliga data i din cache.
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 åtgärden för inbyggd genomläsning och -skrivning.
- 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 gör inga antaganden om vilka data ett program kräver i förväg.
Det här mönstret är kanske inte lämpligt om:
- Om data är känsliga eller säkerhetsrelaterade. Det kan vara olämpligt att lagra den i en cache, särskilt om cachen delas mellan flera program eller användare. Gå alltid till den primära datakällan.
- När cachelagrade data är statiska. 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.
- När de flesta begäranden inte skulle uppleva 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.
- Vid cachelagring av sessionstillståndsinformation i ett webbprogram som finns i en webbgrupp. I den här miljön bör du undvika att introducera beroenden som baseras på tillhörighet mellan klient och server.
Design av arbetsbelastning
En arkitekt bör utvärdera hur det Cache-Aside mönstret kan användas i en design för att uppfylla de mål och principer som beskrivs i Grundpelarna i Azure Well-Architected Framework. Till exempel:
| Grundpelare | Så här stöder det här mönstret pelarmål |
|---|---|
| Beslut om tillförlitlighetsdesign hjälper din arbetsbelastning att bli motståndskraftig mot fel och se till att den återställs till ett fullt fungerande tillstånd när ett fel inträffar. | Cachelagring skapar datareplikering och kan på begränsade sätt användas för att bevara tillgängligheten för data som används ofta om ursprungsdatalagret är tillfälligt otillgängligt. Om det uppstår ett fel i cacheminnet kan arbetsbelastningen dessutom återgå till ursprungsdatalagret. - RE:05 Redundans |
| Prestandaeffektivitet hjälper din arbetsbelastning att effektivt uppfylla kraven genom optimeringar inom skalning, data och kod. | Att använda en cachehytt förbättrar prestandan för läsintensiva data som ändras sällan och kan tolerera viss inaktuellhet. - PE:08 Dataprestanda - PE:12 Kontinuerlig prestandaoptimering |
Som med alla designbeslut bör du överväga eventuella kompromisser mot målen för de andra pelarna som kan införas med det här mönstret.
Exempel
Överväg att använda Azure Managed Redis för att skapa en distribuerad cache som flera programinstanser kan dela.
I följande kodexempel 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. En metod för 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 kodexempel 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 att läsa igenom.
Ett objekt identifieras med hjälp av ett heltals-ID som nyckel. Metoden GetMyEntityAsync försöker hämta ett objekt med den här nyckeln från cachen. Om ett matchande objekt hittas returnerar cacheminnet det. Om det inte finns någon matchning i cachen hämtar metoden GetMyEntityAsync objektet från ett datalager, lägger till det i cachen och returnerar det sedan. Koden som läser data från datalagret visas inte här, eftersom den beror på 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;
}
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 och Använda Azure Redis i .NET Core.
Metoden UpdateEntityAsync som visas nedan 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 togs bort från cachen). Cachemissen gör att den tidigare versionen av objektet hämtas från datalagret och läggs tillbaka till cacheminnet. Resultatet är inaktuella cachedata.
Relaterade resurser
Följande information kan vara relevant när du implementerar det här mönstret:
Tillförlitligt webbappmönster visar hur du använder cache-aside-mönstret för webbprogram som konvergerar i molnet.
Riktlinjer för cachelagring. Tillhandahåller ytterligare information om hur du kan cachelagra data i en molnlösning och de problem som du bör ha i åtanke när du implementerar en cache.
Datakonsekvensprimer. Molnprogram lagrar vanligtvis data över flera datalager och platser. Att hantera och upprätthålla datakonsekvens i den här miljön är en viktig aspekt av systemet, särskilt samtidighets- och tillgänglighetsproblem som kan uppstå. Primern beskriver problemet med konsekvens mellan distribuerade data och sammanfattar hur ett program kan implementera eventuell konsekvens för att upprätthålla datatillgänglighet.
Använd 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.