Zum Hauptinhalt springen

Verteiltes Caching in .NET

· 4 Minuten Lesezeit

Caching ist eine der wirksamsten Methoden, die Performance einer Anwendung zu verbessern.
Die meisten .NET-Anwendungen beginnen mit einem einfachen In-Memory-Cache über MemoryCache oder IMemoryCache. Das funktioniert einwandfrei, solange die Anwendung auf einem einzelnen Server läuft.

Moderne Anwendungen laufen jedoch selten auf nur einem Rechner.

Skaliert eine Anwendung horizontal über mehrere Instanzen, funktioniert In-Memory-Caching nicht mehr zuverlässig. Jede Instanz führt ihren eigenen Cache, sodass der Anwendungszustand im Cluster inkonsistent wird.

Hier wird verteiltes Caching unverzichtbar.


Verteilter Cache


Das Problem mit In-Memory-Caches

Betrachten Sie eine typische ASP.NET-Anwendung hinter einem Load Balancer.

          Load Balancer
|
---------------------------
| | |
Server A Server B Server C

Jeder Server hat seinen eigenen Memory-Cache.

Aktualisiert ein Request zwischengespeicherte Daten auf Server A, bleiben die Caches auf Server B und Server C veraltet.

Das führt zu mehreren Problemen:

  • inkonsistenter Anwendungszustand
  • veraltete Cache-Einträge
  • doppeltes Cache-Warmup
  • unvorhersehbares Verhalten

Um dieses Problem zu lösen, muss der Cache von allen Nodes gemeinsam genutzt werden.


Was ist ein verteilter Cache?

Ein verteilter Cache ist ein zentraler oder geclusterter Cache, auf den mehrere Anwendungsinstanzen zugreifen können.

Statt zwischengespeicherte Daten im Anwendungsprozess abzulegen, werden sie in einem verteilten Key/Value-Store gespeichert.

           Application Nodes

App A App B App C
| | |
+---------+---------+
|
Distributed Cache

Alle Anwendungs-Nodes lesen und schreiben Cache-Einträge im selben verteilten Store.

Das gewährleistet:

  • konsistente zwischengespeicherte Daten
  • gemeinsam genutzten Anwendungszustand
  • bessere Skalierbarkeit

Einfaches Beispiel für einen verteilten Cache in .NET

Ein verteilter Cache wird typischerweise über eine Abstraktion wie IDistributedCache verwendet.

Beispiel:

public class ProductService
{
private readonly IDistributedCache _cache;

public ProductService(IDistributedCache cache)
{
_cache = cache;
}

public async Task<string?> GetProductAsync(string id)
{
var cached = await _cache.GetStringAsync(id);

if (cached != null)
return cached;

var product = await LoadProductFromDatabase(id);

await _cache.SetStringAsync(id, product);

return product;
}

private Task<string> LoadProductFromDatabase(string id)
{
return Task.FromResult($"Product {id}");
}
}

Die Anwendung prüft zuerst den Cache. Fehlt der Wert, werden die Daten aus der Datenbank geladen und anschließend im Cache abgelegt.

Dieses Muster heißt Cache-Aside-Caching.


Gängige Lösungen für verteilte Caches

Mehrere Technologien bieten verteilte Caching-Funktionen.

Redis​

Redis ist einer der am häufigsten eingesetzten verteilten Caches.

Es bietet:

  • Key/Value-Speicherung
  • hohe Performance
  • In-Memory-Operationen

Für die verteilte Koordination benötigt Redis jedoch oft zusätzliche Komponenten.


Datenbankgestützte Caches​

Manche Anwendungen nutzen relationale Datenbanken als Cache.

Das ist zwar einfach, bringt aber:

  • Datenbank-Contention
  • geringere Performance
  • eingeschränkte Skalierbarkeit

Verteilte Key/Value-Plattformen​

Moderne verteilte Systeme nutzen häufig verteilte Key/Value-Plattformen, die sowohl Caching- als auch Koordinationsprimitive bereitstellen.

Diese Systeme unterstützen:

  • verteiltes Caching
  • verteilte Locks
  • Leader-Election
  • Änderungsbenachrichtigungen
  • Cluster-Koordination

Clustron als verteilten Cache verwenden

Clustron bietet eine verteilte Key/Value-Plattform, die für .NET- Anwendungen entwickelt wurde.

Da sie als verteilter Cluster läuft, greifen alle Anwendungsinstanzen auf denselben gemeinsamen Datenspeicher zu.

Beispiel:

using Clustron.DKV.Client;

var client = await DKVClient.InitializeRemote(
"teststore",
new[]
{
new DkvServerInfo("localhost", 7861)
});

await client.PutAsync("product:1", "Laptop");

var value = await client.GetAsync<string>("product:1");

Console.WriteLine(value);

In diesem Beispiel gilt:

  • Anwendungs-Nodes verbinden sich mit dem Cluster
  • Daten werden im verteilten Store gespeichert
  • alle Nodes sehen dieselben Daten

So bleibt der Cache im gesamten Cluster konsistent.


Wann verteiltes Caching sinnvoll ist

Verteiltes Caching ist besonders nützlich in Systemen, die:

  • mehrere Anwendungsinstanzen betreiben
  • hohe Request-Volumina verarbeiten
  • stark auf Datenbank-Lesezugriffe angewiesen sind
  • gemeinsamen Anwendungszustand benötigen

Typische Anwendungsfälle sind:

  • ASP.NET-Webanwendungen
  • Microservice-Architekturen
  • Systeme zur Hintergrundverarbeitung
  • API-Plattformen

Zusammenfassung

In-Memory-Caching eignet sich gut für Anwendungen auf einem einzelnen Server, doch moderne verteilte Systeme benötigen eine gemeinsame Caching-Schicht.

Verteiltes Caching erlaubt es mehreren Anwendungsinstanzen, dieselben zwischengespeicherten Daten zu nutzen, und verbessert so Skalierbarkeit und Konsistenz.

Plattformen wie Clustron stellen einen verteilten Key/Value-Store bereit, der verteiltes Caching und Koordination in .NET-Anwendungen einfach umsetzbar macht.