Redigera

Dela via


Mönstret Cache-Aside

Azure Cache for Redis

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 däremot opraktiskt att förvänta sig att cachelagrade data alltid är fullständigt konsekventa med de data som lagras i datalagret. Program bör implementera en strategi som bidrar till att säkerställa att cachelagrade data är så uppdaterade som möjligt, men att de också kan upptäcka och hantera situationer som uppstår när cachelagrade data har blivit inaktuella.

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 dina data inte finns i cacheminnet hämtas de från datalagret och läggs till i cachen. Ä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.

Använda Cache-Aside-mönstret för att lagra data i cachelagringen

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.

Om ett objekt är det nästa att begäras kommer uppdaterade data att hämtas från datalagret och föras tillbaka till cachelagringen om du använder cache-aside-strategin.

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 cachen implementerar en förfalloprincip som upphäver data och tar bort dem från cachen om de inte använts under en viss 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, då det kan leda till att programmen hämtar data regelbundet från datalagret och lägger till dem i cachen. 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 informationen har sitt ursprung och de tar bort data när det behövs. De flesta cacheminnen väljer vilka objekt som ska tas bort efter en princip om senast använda, men det kan anpassas. Säkerställ att cachen är kostnadseffektiv genom att konfigurera den globala förfalloegenskapen och andra egenskaper för cachen samt förfalloegenskapen för varje cacheobjekt. Det är inte alltid lämpligt att tillämpa en global förfallopolicy till varje objekt i cachen. Om ett cacheobjekt till exempel är väldigt dyrt att hämta från datalagret kan det vara fördelaktigt att behålla objektet i cachen på bekostnad av mer frekvent använda, men 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. Ett objekt i datalagret kan när som helst ändras av en extern process och ändringen kanske inte reflekteras i cachen förrän nästa gång objektet laddas. I ett system som replikerar data mellan datalager kan det här problemet bli allvarligt om synkronisering uppstår regelbundet.

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.

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örs inga antaganden i förväg om vilka data som ett program kräver.

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

  • När cachelagrade data är statiska. Om dina data passar in i tillgängligt cacheutrymme ska du prima cachen med dina startup-data och tillämpa en policy som hindrar informationen från att upphöra att gälla.
  • För tillståndsinformation om en cachelagringssession i ett webbprogram med en webbservergrupp som värd. 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 Cache-Aside-mönstret kan användas i arbetsbelastningens 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. Bättre prestanda i din arbetsbelastning kan erhållas när du använder en cache för läsintensiva data som inte ändras ofta och din arbetsbelastning är utformad för att tolerera en 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

I Microsoft Azure kan du använda Azure Cache for Redis för att skapa en distribuerad cache som kan delas av flera instanser av ett program.

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 Cache for Redis-instans anropar du den statiska ConnectionMultiplexer.Connect metoden och skickar in anslutningssträng. 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 har det returnerats. 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 faktiskt läser data från datalagringen visas inte här, eftersom den är beroende av datalagringen. Observera att det cachelagrade objektet är konfigurerat till att förfalla, så att det inte blir inaktuellt om det uppdateras på en annan plats.

// 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 Cache for Redis för att komma åt arkivet och hämta information från cacheminnet. Mer information finns i Använda Azure Cache for Redis och Så här skapar du en webbapp med Azure Cache for Redis.

Metoden UpdateEntityAsync nedan visar hur du upphäver ett objekt i cachen när värdet ändras av programmet. 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 först tar bort det cachelagrade objektet finns en liten tidsram när en klient kan hämta objektet innan datalagret uppdateras. Det leder till en cachemiss (eftersom objektet togs bort från cachen) och gör så att en tidigare objektversion hämtas från datalagringen och förs tillbaka till cachen. Resultatet gör cachelagrade data inaktuella.

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 använder vanligtvis data som är utspridda över datalager. Hantera och underhålla datakonsekvens i den här miljön är en viktig aspekt av systemet, särskilt problem som kan uppstå med samtidighet och tillgänglighet. 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.