Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Ten wzorzec ładuje dane na żądanie do pamięci podręcznej z magazynu danych. Użyj tego wzorca, aby zwiększyć wydajność i zapewnić spójność między danymi w pamięci podręcznej i danych w bazowym magazynie danych.
Kontekst i problem
Aplikacje używają pamięci podręcznej, aby zwiększyć wydajność w celu wielokrotnego dostępu do informacji w magazynie danych. Jednak buforowane dane nie zawsze mogą pozostać spójne z magazynem danych. Aplikacje powinny zaimplementować strategię, która przechowuje dane w pamięci podręcznej tak aktualne, jak to możliwe. Strategia powinna również wykrywać, kiedy buforowane dane staną się nieaktualne i odpowiednio je obsłużyć.
Rozwiązanie
Wiele komercyjnych systemów buforowania zapewnia operacje odczytu z buforem, zapisu z buforem lub zapisu asynchronicznego. W tych systemach aplikacja pobiera dane poprzez odwołanie do pamięci podręcznej. Jeśli dane nie są w pamięci podręcznej, aplikacja pobiera je z magazynu danych i dodaje je do pamięci podręcznej. System automatycznie zapisuje wszelkie zmiany wprowadzone w buforowanych danych z powrotem do magazynu danych.
W przypadku pamięci podręcznych, które nie zapewniają tej funkcji, aplikacje korzystające z pamięci podręcznej muszą przechowywać dane.
Aplikacja może emulować buforowanie typu read-through, stosując wzorzec Cache-Aside. Ta strategia ładuje dane do pamięci podręcznej na żądanie. Na poniższym diagramie użyto wzorca Cache-Aside do przechowywania danych w pamięci podręcznej.
Aplikacja określa, czy element znajduje się obecnie w pamięci podręcznej, próbując odczytać z pamięci podręcznej.
Jeśli element nie znajduje się w pamięci podręcznej, co jest również znane jako brak w pamięci podręcznej, aplikacja pobiera element z magazynu danych.
Aplikacja dodaje element do pamięci podręcznej, a następnie zwraca go do obiektu wywołującego.
Jeśli aplikacja aktualizuje informacje, może ona postępować zgodnie ze strategią zapisu, wprowadzając modyfikację magazynu danych i unieważniając odpowiedni element w pamięci podręcznej.
Gdy element będzie potrzebny ponownie, wzorzec Cache-Aside pobiera zaktualizowane dane z magazynu danych i dodaje go do pamięci podręcznej.
Problemy i zagadnienia
Podczas podejmowania decyzji o zaimplementowaniu tego wzorca należy wziąć pod uwagę następujące kwestie:
Okres istnienia buforowanych danych: Wiele pamięci podręcznych używa zasad wygasania, aby unieważniać dane i usuwać je z pamięci podręcznej, jeśli nie są dostępne przez określony okres. Aby zapewnić efektywność strategii cache-aside, upewnij się, że zasady wygasania są zgodne ze wzorcem dostępu używanym przez aplikacje. Nie należy ustawiać zbyt krótkiego okresu wygaśnięcia, ponieważ przedwczesne wygaśnięcie może spowodować, że aplikacje będą stale pobierać dane z magazynu danych i dodawać je do pamięci podręcznej. Podobnie, nie ustawiaj okresu wygaśnięcia tak długiego, aby buforowane dane stały się nieaktualne. Buforowanie działa najlepiej w przypadku stosunkowo statycznych danych lub danych często odczytywanych przez aplikacje.
Eksmitowanie danych: Większość pamięci podręcznych ma ograniczony rozmiar w porównaniu z magazynem danych, z którego pochodzą dane. Jeśli pamięć podręczna przekroczy limit rozmiaru, eksmituje dane. Większość pamięci podręcznych stosuje co najmniej ostatnio używane zasady do wybierania elementów w celu eksmisji, ale niektóre zezwalają na dostosowywanie.
Konfiguracja: Zachowanie pamięci podręcznej można skonfigurować globalnie lub na element buforowany. Jedna globalna zasada eksmisji może nie odpowiadać wszystkim elementom. Jeśli element jest kosztowny do pobrania, skonfiguruj element pamięci podręcznej indywidualnie. W takiej sytuacji warto zachować element w pamięci podręcznej, nawet jeśli dostęp do niego jest rzadziej używany niż tańsze elementy.
Wstępne ładowanie pamięci podręcznej: Wiele rozwiązań wstępnie ładuje pamięć podręczną danymi, których aplikacja prawdopodobnie wymaga w ramach procesu uruchamiania. Wzorzec Cache-Aside jest przydatny, gdy niektóre z tych danych wygasają lub są usuwane.
Spójność: Wzorzec Cache-Aside nie gwarantuje zachowania spójności między magazynem danych a pamięcią podręczną. Na przykład proces zewnętrzny może w dowolnym momencie zmienić element w magazynie danych. Ta zmiana nie jest wyświetlana w pamięci podręcznej, dopóki element nie zostanie załadowany ponownie. W systemie, który replikuje dane w magazynach danych, częste synchronizacje mogą utrudnić spójność.
Buforowanie lokalne: Pamięć podręczna może być lokalna dla konkretnego wystąpienia aplikacji i przechowywana w pamięci. Cache-aside działa dobrze w tym kontekście, jeśli aplikacja wielokrotnie uzyskuje dostęp do tych samych danych. Jednak lokalna pamięć podręczna jest prywatna, więc różne wystąpienia aplikacji mogą mieć kopię tych samych buforowanych danych. Te dane mogą szybko stać się niespójne między pamięciami podręcznymi, więc może być konieczne wygasnąć dane w prywatnej pamięci podręcznej i odświeżyć je częściej. W tych scenariuszach rozważ użycie mechanizmu buforowania współużytkowanego lub rozproszonego.
Buforowanie semantyczne: Niektóre obciążenia mogą czerpać korzyści z pobierania danych z pamięci podręcznej na podstawie znaczenia semantycznego, a nie dokładnych kluczy. Takie podejście zmniejsza liczbę żądań i tokenów wysyłanych do modeli językowych. Buforowanie semantyczne jest używane tylko wtedy, gdy dane obsługują równoważność semantyczną, nie ryzykuje zwracania niepowiązanych odpowiedzi i nie zawiera prywatnych i poufnych danych. Na przykład "Jakie jest moje roczne wynagrodzenie netto?" jest semantycznie podobne do "Jaka jest moja roczna wypłata netto?" Jeśli jednak różni użytkownicy zadają te pytania, odpowiedzi powinny się różnić. Nie należy również uwzględniać tych poufnych danych w pamięci podręcznej.
Kiedy używać tego wzorca
Użyj tego wzorca, gdy:
Pamięć podręczna nie zapewnia natywnych operacji read-through i write-through.
Żądanie zasobu jest nieprzewidywalne. Ten wzorzec umożliwia aplikacjom ładowanie danych na żądanie. Nie zakłada, które dane wymaga aplikacja z wyprzedzeniem.
Ten wzorzec może nie być odpowiedni w następujących przypadkach:
Dane są poufne lub związane z zabezpieczeniami. Przechowywanie danych w pamięci podręcznej może być nieodpowiednie, zwłaszcza gdy wiele aplikacji lub użytkowników współużytkuje pamięć podręczną. Zawsze pobieraj dane tego typu z podstawowego źródła.
Buforowany zestaw danych jest statyczny. Jeśli dane mieszczą się w dostępnej przestrzeni pamięci podręcznej, należy zastosować zasady uniemożliwiające wygaśnięcie danych w pamięci podręcznej przy uruchamianiu.
Większość żądań nie odnotowuje trafienia w pamięci podręcznej. W takiej sytuacji obciążenie związane z sprawdzaniem pamięci podręcznej i ładowaniem danych do niej może przewyższać korzyści wynikające z buforowania.
Informacje o stanie sesji są buforowane w aplikacji internetowej hostowanej w farmie sieci Web. W tym środowisku unikaj wprowadzania zależności na podstawie koligacji klient-serwer.
Projektowanie obciążenia pracy
Oceń, jak używać wzorca Cache-Aside w projekcie obciążenia, aby sprostać celom i zasadom opisanym w filarach platformy Azure Well-Architected Framework. Poniższa tabela zawiera wskazówki dotyczące tego, jak ten wzorzec obsługuje cele poszczególnych filarów.
| Filar | Jak ten wzorzec obsługuje cele filaru |
|---|---|
| Decyzje projektowe dotyczące niezawodności pomagają obciążeniom stały się odporne na awarię i zapewniają, że zostanie ono przywrócone do w pełni funkcjonalnego stanu po wystąpieniu awarii. | Buforowanie replikuje dane. W ograniczony sposób może zachować dostępność często używanych danych, jeśli magazyn danych pochodzenia stanie się tymczasowo niedostępny. Jeśli pamięć podręczna nie działa, obciążenie może wrócić do magazynu danych pochodzenia. - RE:05 Redundancja |
| Efektywność wydajności pomaga wydajnie sprostać wymaganiom dzięki optymalizacjom skalowania, danych i kodu. | Buforowanie zwiększa wydajność danych z dużą liczbą odczytów, które zmieniają się rzadko i tolerują pewne nieaktualności. - PE:08 Wydajność danych - PE:12 Ciągła optymalizacja wydajności |
Jeśli ten wzorzec wprowadza kompromisy w ramach filaru, rozważ je przed celami innych filarów.
Przykład
Rozważ użycie usługi Azure Managed Redis do utworzenia rozproszonej pamięci podręcznej, którą może współużytkować wiele wystąpień aplikacji.
W poniższym przykładzie użyto klienta StackExchange.Redis , który jest biblioteką klienta Redis napisaną dla platformy .NET. Aby nawiązać połączenie z wystąpieniem usługi Azure Managed Redis, wywołaj metodę statyczną ConnectionMultiplexer.Connect i przekaż parametry połączenia. Metoda zwraca ConnectionMultiplexer reprezentujący połączenie.
Jednym ze sposobów udostępniania wystąpienia ConnectionMultiplexer w aplikacji jest utworzenie właściwości statycznej, która zwraca połączone wystąpienie, podobnie jak w poniższym przykładzie. To podejście oferuje bezpieczne dla wątków sposób na inicjowanie tylko jednej połączonej instancji.
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;
Metoda GetMyEntityAsync w poniższym przykładzie przedstawia implementację wzorca Cache-Aside. Ta metoda pobiera obiekt z pamięci podręcznej przy użyciu podejścia odczytu bezpośredniego.
Metoda identyfikuje obiekt przy użyciu identyfikatora całkowitego jako klucza. Próbuje pobrać element z pamięci podręcznej przy użyciu tego klucza. Jeśli pamięć podręczna zawiera pasujący element, zwraca element. Jeśli pamięć podręczna nie zawiera dopasowania, GetMyEntityAsync metoda pobiera obiekt z magazynu danych, dodaje go do pamięci podręcznej, a następnie zwraca. W tym przykładzie pominięto kod odczytujący dane z magazynu danych, ponieważ ta logika zależy od magazynu danych. Buforowany element jest skonfigurowany do wygaśnięcia, aby zapobiec jego nieaktualności, jeśli inna usługa lub proces go zaktualizuje.
// 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;
}
Uwaga
W przykładach użyto usługi Azure Managed Redis, aby uzyskać dostęp do magazynu i pobrać informacje z pamięci podręcznej. Aby uzyskać więcej informacji, zobacz Tworzenie wystąpienia usługi Azure Managed Redis i Używanie usługi Azure Managed Redis na platformie .NET Core.
Poniższa UpdateEntityAsync metoda pokazuje, jak unieważnić obiekt w pamięci podręcznej, gdy aplikacja zmieni wartość. Kod aktualizuje oryginalne źródło danych, a następnie usuwa element z pamięci podręcznej.
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.
}
Uwaga
Kolejność kroków jest ważna. Zaktualizuj magazyn danych przed usunięciem elementu z pamięci podręcznej. Jeśli najpierw usuniesz buforowany element, zostanie wyświetlone małe okno czasu, w którym klient może pobrać element przed zaktualizowaniem magazynu danych. W takiej sytuacji pobieranie powoduje pominięcie pamięci podręcznej, ponieważ element nie znajduje się w pamięci podręcznej. Brak pamięci podręcznej powoduje, że aplikacja pobiera nieaktualny element z magazynu danych i dodaje go z powrotem do pamięci podręcznej. Ta sekwencja prowadzi do nieaktualnych danych w pamięci podręcznej.
Następne kroki
Podstawy spójności danych: Poradnik ten opisuje problemy ze spójnością danych w rozproszonych bazach danych. Podsumowuje również, jak aplikacja może zaimplementować spójność ostateczną w celu zachowania dostępności danych. Aplikacje w chmurze zwykle przechowują dane w wielu magazynach danych i lokalizacjach. Musisz efektywnie zarządzać spójnością danych w tym środowisku i utrzymywać je, szczególnie z powodu problemów ze współbieżnością i dostępnością, które mogą wystąpić.
Używanie usługi Azure Managed Redis jako pamięci podręcznej semantycznej: w tym samouczku pokazano, jak zaimplementować buforowanie semantyczne przy użyciu usługi Azure Managed Redis.
Powiązane zasoby
Wzorzec niezawodnej aplikacji internetowej: ten wzorzec stosuje wzorzec Cache-Aside do aplikacji internetowych w chmurze.
Wskazówki dotyczące buforowania: te wskazówki zawierają więcej informacji na temat buforowania danych w rozwiązaniu w chmurze oraz problemów, które należy wziąć pod uwagę podczas implementowania pamięci podręcznej.